diff --git a/src/xenia/gpu/gl4/command_processor.cc b/src/xenia/gpu/gl4/command_processor.cc index 7ceacd648..51391ea03 100644 --- a/src/xenia/gpu/gl4/command_processor.cc +++ b/src/xenia/gpu/gl4/command_processor.cc @@ -2400,6 +2400,7 @@ bool CommandProcessor::IssueCopy() { // Depending on the source, pick the buffer we'll be sourcing. // We then query for a cached framebuffer setup with that buffer active. + TextureFormat src_format = TextureFormat::kUnknown; GLuint color_targets[4] = {kAnyTarget, kAnyTarget, kAnyTarget, kAnyTarget}; GLuint depth_target = kAnyTarget; if (copy_src_select <= 3) { @@ -2414,6 +2415,7 @@ bool CommandProcessor::IssueCopy() { (color_info[copy_src_select] >> 16) & 0xF); color_targets[copy_src_select] = GetColorRenderTarget( surface_pitch, surface_msaa, color_base, color_format); + src_format = ColorRenderTargetToTextureFormat(color_format); } else { // Source from depth/stencil. uint32_t depth_info = regs[XE_GPU_REG_RB_DEPTH_INFO].u32; @@ -2422,6 +2424,7 @@ bool CommandProcessor::IssueCopy() { static_cast((depth_info >> 16) & 0x1); depth_target = GetDepthRenderTarget(surface_pitch, surface_msaa, depth_base, depth_format); + src_format = DepthRenderTargetToTextureFormat(depth_format); } auto source_framebuffer = GetFramebuffer(color_targets, depth_target); if (!source_framebuffer) { @@ -2468,7 +2471,7 @@ bool CommandProcessor::IssueCopy() { glPixelStorei(GL_PACK_SWAP_BYTES, GL_TRUE); break; default: - assert_unhandled_case(copy_dest_endian); + //assert_unhandled_case(copy_dest_endian); glPixelStorei(GL_PACK_SWAP_BYTES, GL_TRUE); break; } @@ -2493,31 +2496,68 @@ bool CommandProcessor::IssueCopy() { uint32_t w = copy_dest_pitch; uint32_t h = copy_dest_height; - if (!FLAGS_disable_framebuffer_readback) { - // Make active so glReadPixels reads from us. - glBindFramebuffer(GL_READ_FRAMEBUFFER, source_framebuffer->framebuffer); - switch (copy_command) { - case CopyCommand::kConvert: - if (copy_src_select <= 3) { - // Source from a bound render target. - // glBindBuffer(GL_READ_FRAMEBUFFER, framebuffer) - glNamedFramebufferReadBuffer(source_framebuffer->framebuffer, - GL_COLOR_ATTACHMENT0 + copy_src_select); - glReadPixels(x, y, w, h, read_format, read_type, ptr); - } else { - // Source from the bound depth/stencil target. - glReadPixels(x, y, w, h, GL_DEPTH_STENCIL, read_type, ptr); + // Make active so glReadPixels reads from us. + glBindFramebuffer(GL_READ_FRAMEBUFFER, source_framebuffer->framebuffer); + switch (copy_command) { + case CopyCommand::kRaw: { + // This performs a byte-for-byte copy of the textures from src to dest + // with no conversion. Byte swapping may still occur. + if (copy_src_select <= 3) { + // Source from a bound render target. + glNamedFramebufferReadBuffer(source_framebuffer->framebuffer, + GL_COLOR_ATTACHMENT0 + copy_src_select); + // TODO(benvanik): RAW copy. + texture_cache_.CopyReadBufferTexture( + copy_dest_base, x, y, w, h, + ColorFormatToTextureFormat(copy_dest_format), + copy_dest_swap ? true : false); + if (!FLAGS_disable_framebuffer_readback) { + // glReadPixels(x, y, w, h, read_format, read_type, ptr); } - break; - case CopyCommand::kRaw: - case CopyCommand::kConstantOne: - case CopyCommand::kNull: - default: - // assert_unhandled_case(copy_command); - return false; + } else { + // Source from the bound depth/stencil target. + // TODO(benvanik): RAW copy. + texture_cache_.CopyReadBufferTexture(copy_dest_base, x, y, w, h, + src_format, + copy_dest_swap ? true : false); + if (!FLAGS_disable_framebuffer_readback) { + // glReadPixels(x, y, w, h, GL_DEPTH_STENCIL, read_type, ptr); + } + } + break; } - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + case CopyCommand::kConvert: { + if (copy_src_select <= 3) { + // Source from a bound render target. + glNamedFramebufferReadBuffer(source_framebuffer->framebuffer, + GL_COLOR_ATTACHMENT0 + copy_src_select); + // Either copy the readbuffer into an existing texture or create a new + // one in the cache so we can service future upload requests. + texture_cache_.CopyReadBufferTexture( + copy_dest_base, x, y, w, h, + ColorFormatToTextureFormat(copy_dest_format), + copy_dest_swap ? true : false); + if (!FLAGS_disable_framebuffer_readback) { + // glReadPixels(x, y, w, h, read_format, read_type, ptr); + } + } else { + // Source from the bound depth/stencil target. + texture_cache_.CopyReadBufferTexture(copy_dest_base, x, y, w, h, + src_format, + copy_dest_swap ? true : false); + if (!FLAGS_disable_framebuffer_readback) { + // glReadPixels(x, y, w, h, GL_DEPTH_STENCIL, read_type, ptr); + } + } + break; + } + case CopyCommand::kConstantOne: + case CopyCommand::kNull: + default: + // assert_unhandled_case(copy_command); + return false; } + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); // Perform any requested clears. uint32_t copy_depth_clear = regs[XE_GPU_REG_RB_DEPTH_CLEAR].u32; diff --git a/src/xenia/gpu/gl4/texture_cache.cc b/src/xenia/gpu/gl4/texture_cache.cc index 8f84ad76e..136b7f54a 100644 --- a/src/xenia/gpu/gl4/texture_cache.cc +++ b/src/xenia/gpu/gl4/texture_cache.cc @@ -22,6 +22,125 @@ using namespace xe::gpu::xenos; extern "C" GLEWContext* glewGetContext(); extern "C" WGLEWContext* wglewGetContext(); +struct TextureConfig { + TextureFormat texture_format; + GLenum internal_format; + GLenum format; + GLenum type; +}; + +// https://code.google.com/p/glsnewton/source/browse/trunk/Source/uDDSLoader.pas?r=62 +// http://dench.flatlib.jp/opengl/textures +// http://fossies.org/linux/WebKit/Source/ThirdParty/ANGLE/src/libGLESv2/formatutils.cpp +static const TextureConfig texture_configs[64] = { + { TextureFormat::k_1_REVERSE, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_8, GL_R8, GL_RED, GL_UNSIGNED_BYTE }, + { TextureFormat::k_1_5_5_5, GL_RGB5_A1, GL_RGBA, + GL_UNSIGNED_SHORT_1_5_5_5_REV }, + { TextureFormat::k_5_6_5, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5 }, + { TextureFormat::k_6_5_5, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_8_8_8_8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, + { TextureFormat::k_2_10_10_10, GL_RGB10_A2, GL_RGBA, + GL_UNSIGNED_INT_2_10_10_10_REV }, + { TextureFormat::k_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_8_B, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_8_8, GL_RG8, GL_RG, GL_UNSIGNED_BYTE }, + { TextureFormat::k_Cr_Y1_Cb_Y0, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_Y1_Cr_Y0_Cb, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_8_8_8_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_4_4_4_4, GL_RGBA4, GL_RGBA, + GL_UNSIGNED_SHORT_4_4_4_4_REV }, + { TextureFormat::k_10_11_11, GL_R11F_G11F_B10F, GL_RGB, + GL_UNSIGNED_INT_10F_11F_11F_REV }, // ? + { TextureFormat::k_11_11_10, GL_R11F_G11F_B10F, GL_RGB, + GL_UNSIGNED_INT_10F_11F_11F_REV }, // ? + { TextureFormat::k_DXT1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, + GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE }, + { TextureFormat::k_DXT2_3, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE }, + { TextureFormat::k_DXT4_5, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE }, + { TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_24_8, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, + GL_UNSIGNED_INT_24_8 }, + { TextureFormat::k_24_8_FLOAT, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, + GL_FLOAT_32_UNSIGNED_INT_24_8_REV }, + { TextureFormat::k_16, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_16_16, GL_RG16, GL_RG, GL_UNSIGNED_SHORT }, + { TextureFormat::k_16_16_16_16, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_16_EXPAND, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_16_16_EXPAND, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_16_16_16_16_EXPAND, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_16_FLOAT, GL_R16F, GL_RED, GL_HALF_FLOAT }, + { TextureFormat::k_16_16_FLOAT, GL_RG16F, GL_RG, GL_HALF_FLOAT }, + { TextureFormat::k_16_16_16_16_FLOAT, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT }, + { TextureFormat::k_32, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_32_32, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_32_32_32_32, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_32_FLOAT, GL_R32F, GL_RED, GL_FLOAT }, + { TextureFormat::k_32_32_FLOAT, GL_RG32F, GL_RG, GL_FLOAT }, + { TextureFormat::k_32_32_32_32_FLOAT, GL_RGBA32F, GL_RGBA, GL_FLOAT }, + { TextureFormat::k_32_AS_8, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_32_AS_8_8, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_16_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_32_AS_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_32_AS_8_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_16_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_16_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::k_DXN, GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_RG_RGTC2, + GL_INVALID_ENUM }, + { TextureFormat::k_8_8_8_8_AS_16_16_16_16, GL_RGBA8, GL_RGBA, + GL_UNSIGNED_BYTE }, + { TextureFormat::k_DXT1_AS_16_16_16_16, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, + GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE }, + { TextureFormat::k_DXT2_3_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE }, + { TextureFormat::k_DXT4_5_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE }, + { TextureFormat::k_2_10_10_10_AS_16_16_16_16, GL_RGB10_A2, GL_RGBA, + GL_UNSIGNED_INT_2_10_10_10_REV }, + { TextureFormat::k_10_11_11_AS_16_16_16_16, GL_R11F_G11F_B10F, GL_RGB, + GL_UNSIGNED_INT_10F_11F_11F_REV }, + { TextureFormat::k_11_11_10_AS_16_16_16_16, GL_R11F_G11F_B10F, + GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_32_32_32_FLOAT, GL_RGB32F, GL_RGB, GL_FLOAT }, + { TextureFormat::k_DXT3A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_DXT5A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_CTX1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM }, + { TextureFormat::k_DXT3A_AS_1_1_1_1, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, + { TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM }, +}; + TextureCache::TextureCache() : memory_(nullptr), scratch_buffer_(nullptr) { invalidated_textures_sets_[0].reserve(64); invalidated_textures_sets_[1].reserve(64); @@ -70,7 +189,7 @@ void TextureCache::Clear() { void TextureCache::EvictAllTextures() { // Kill all textures - some may be in the eviction list, but that's fine // as we will clear that below. - while (texture_entries_.size()) { + while (!texture_entries_.empty()) { auto entry = texture_entries_.begin()->second; EvictTexture(entry); } @@ -80,6 +199,15 @@ void TextureCache::EvictAllTextures() { invalidated_textures_sets_[0].clear(); invalidated_textures_sets_[1].clear(); } + + // Kill all readbuffer textures. + while (!read_buffer_textures_.empty()) { + auto it = --read_buffer_textures_.end(); + auto entry = *it; + glDeleteTextures(1, &entry->handle); + delete entry; + read_buffer_textures_.erase(it); + } } TextureCache::TextureEntryView* TextureCache::Demand( @@ -278,6 +406,25 @@ TextureCache::TextureEntry* TextureCache::LookupOrInsertTexture( entry->pending_invalidation = false; entry->handle = 0; + // Check read buffer textures - there may be one waiting for us. + // TODO(benvanik): speed up existence check? + for (auto it = read_buffer_textures_.begin(); + it != read_buffer_textures_.end(); ++it) { + auto read_buffer_entry = *it; + if (read_buffer_entry->guest_address == texture_info.guest_address && + read_buffer_entry->width == texture_info.size_2d.logical_width && + read_buffer_entry->height == texture_info.size_2d.logical_height) { + // Found! Acquire the handle and remove the readbuffer entry. + read_buffer_textures_.erase(it); + entry->handle = read_buffer_entry->handle; + delete read_buffer_entry; + // TODO(benvanik): set more texture properties? swizzle/etc? + auto entry_ptr = entry.get(); + texture_entries_.insert({hash, entry.release()}); + return entry_ptr; + } + } + GLenum target; switch (texture_info.dimension) { case Dimension::k1D: @@ -362,6 +509,84 @@ TextureCache::TextureEntry* TextureCache::LookupOrInsertTexture( return entry_ptr; } +TextureCache::TextureEntry* TextureCache::LookupAddress(uint32_t guest_address, + uint32_t width, + uint32_t height, + TextureFormat format) { + // TODO(benvanik): worth speeding up? + for (auto it = texture_entries_.begin(); it != texture_entries_.end(); ++it) { + const auto& texture_info = it->second->texture_info; + if (texture_info.guest_address == guest_address && + texture_info.dimension == Dimension::k2D && + texture_info.size_2d.logical_width == width && + texture_info.size_2d.logical_height == height) { + return it->second; + } + } + return nullptr; +} + +void TextureCache::CopyReadBufferTexture(uint32_t guest_address, uint32_t x, + uint32_t y, uint32_t width, + uint32_t height, + TextureFormat format, + bool swap_channels) { + // See if we have used a texture at this address before. If we have, we can + // reuse it. + // TODO(benvanik): better lookup matching format/etc? + auto texture_entry = LookupAddress(guest_address, width, height, format); + if (texture_entry) { + // Have existing texture. + assert_false(texture_entry->pending_invalidation); + glCopyTextureSubImage2D(texture_entry->handle, 0, 0, 0, x, y, width, + height); + + // HACK: remove texture from write watch list so readback won't kill us. + if (texture_entry->write_watch_handle) { + memory_->CancelWriteWatch(texture_entry->write_watch_handle); + texture_entry->write_watch_handle = 0; + } + return; + } + + // Check pending read buffer textures (for multiple resolves with no + // uploads inbetween). + for (auto it = read_buffer_textures_.begin(); + it != read_buffer_textures_.end(); ++it) { + const auto& entry = *it; + if (entry->guest_address == guest_address && entry->width == width && + entry->height == height && entry->format == format) { + // Found an existing entry - just reupload. + glCopyTextureSubImage2D(entry->handle, 0, 0, 0, x, y, width, height); + return; + } + } + + const auto& config = texture_configs[uint32_t(format)]; + if (config.format == GL_INVALID_ENUM) { + assert_always("Unhandled destination texture format"); + return; + } + + // Need to create a new texture. + // As we don't know anything about this texture, we'll add it to the + // pending readbuffer list. If nobody claims it after a certain amount + // of time we'll dump it. + auto entry = std::make_unique(); + entry->guest_address = guest_address; + entry->width = width; + entry->height = height; + entry->format = format; + + glCreateTextures(GL_TEXTURE_2D, 1, &entry->handle); + glTextureParameteri(entry->handle, GL_TEXTURE_BASE_LEVEL, 0); + glTextureParameteri(entry->handle, GL_TEXTURE_MAX_LEVEL, 1); + glTextureStorage2D(entry->handle, 1, config.internal_format, width, height); + glCopyTextureSubImage2D(entry->handle, 0, 0, 0, x, y, width, height); + + read_buffer_textures_.push_back(entry.release()); +} + void TextureCache::EvictTexture(TextureEntry* entry) { if (entry->write_watch_handle) { memory_->CancelWriteWatch(entry->write_watch_handle); @@ -413,125 +638,6 @@ void TextureSwap(Endian endianness, void* dest, const void* src, } } -struct TextureConfig { - TextureFormat texture_format; - GLenum internal_format; - GLenum format; - GLenum type; -}; - -// https://code.google.com/p/glsnewton/source/browse/trunk/Source/uDDSLoader.pas?r=62 -// http://dench.flatlib.jp/opengl/textures -// http://fossies.org/linux/WebKit/Source/ThirdParty/ANGLE/src/libGLESv2/formatutils.cpp -static const TextureConfig texture_configs[64] = { - {TextureFormat::k_1_REVERSE, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8, GL_R8, GL_RED, GL_UNSIGNED_BYTE}, - {TextureFormat::k_1_5_5_5, GL_RGB5_A1, GL_RGBA, - GL_UNSIGNED_SHORT_1_5_5_5_REV}, - {TextureFormat::k_5_6_5, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, - {TextureFormat::k_6_5_5, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8_8_8_8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, - {TextureFormat::k_2_10_10_10, GL_RGB10_A2, GL_RGBA, - GL_UNSIGNED_INT_2_10_10_10_REV}, - {TextureFormat::k_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8_B, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8_8, GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, - {TextureFormat::k_Cr_Y1_Cb_Y0, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_Y1_Cr_Y0_Cb, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_8_8_8_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_4_4_4_4, GL_RGBA4, GL_RGBA, - GL_UNSIGNED_SHORT_4_4_4_4_REV}, - {TextureFormat::k_10_11_11, GL_R11F_G11F_B10F, GL_RGB, - GL_UNSIGNED_INT_10F_11F_11F_REV}, // ? - {TextureFormat::k_11_11_10, GL_R11F_G11F_B10F, GL_RGB, - GL_UNSIGNED_INT_10F_11F_11F_REV}, // ? - {TextureFormat::k_DXT1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, - GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT2_3, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT4_5, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_24_8, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, - GL_UNSIGNED_INT_24_8}, - {TextureFormat::k_24_8_FLOAT, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, - GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, - {TextureFormat::k_16, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_16_16, GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_16_16_16, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_EXPAND, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_16_EXPAND, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_16_16_16_EXPAND, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_FLOAT, GL_R16F, GL_RED, GL_HALF_FLOAT}, - {TextureFormat::k_16_16_FLOAT, GL_RG16F, GL_RG, GL_HALF_FLOAT}, - {TextureFormat::k_16_16_16_16_FLOAT, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, - {TextureFormat::k_32, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_32_32, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_32_32_32_32, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_FLOAT, GL_R32F, GL_RED, GL_FLOAT}, - {TextureFormat::k_32_32_FLOAT, GL_RG32F, GL_RG, GL_FLOAT}, - {TextureFormat::k_32_32_32_32_FLOAT, GL_RGBA32F, GL_RGBA, GL_FLOAT}, - {TextureFormat::k_32_AS_8, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_AS_8_8, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_AS_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_AS_8_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_DXN, GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_RG_RGTC2, - GL_INVALID_ENUM}, - {TextureFormat::k_8_8_8_8_AS_16_16_16_16, GL_RGBA8, GL_RGBA, - GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT1_AS_16_16_16_16, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, - GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT2_3_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT4_5_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_2_10_10_10_AS_16_16_16_16, GL_RGB10_A2, GL_RGBA, - GL_UNSIGNED_INT_2_10_10_10_REV}, - {TextureFormat::k_10_11_11_AS_16_16_16_16, GL_R11F_G11F_B10F, GL_RGB, - GL_UNSIGNED_INT_10F_11F_11F_REV}, - {TextureFormat::k_11_11_10_AS_16_16_16_16, GL_R11F_G11F_B10F, - GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_32_32_32_FLOAT, GL_RGB32F, GL_RGB, GL_FLOAT}, - {TextureFormat::k_DXT3A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_DXT5A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_CTX1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_DXT3A_AS_1_1_1_1, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, -}; - bool TextureCache::UploadTexture2D(GLuint texture, const TextureInfo& texture_info) { const auto host_address = memory_->Translate(texture_info.guest_address); diff --git a/src/xenia/gpu/gl4/texture_cache.h b/src/xenia/gpu/gl4/texture_cache.h index 4bec06f2d..9b3cf2adc 100644 --- a/src/xenia/gpu/gl4/texture_cache.h +++ b/src/xenia/gpu/gl4/texture_cache.h @@ -57,13 +57,26 @@ class TextureCache { TextureEntryView* Demand(const TextureInfo& texture_info, const SamplerInfo& sampler_info); + void CopyReadBufferTexture(uint32_t guest_address, uint32_t x, uint32_t y, + uint32_t width, uint32_t height, + TextureFormat format, bool swap_channels); private: + struct ReadBufferTexture { + uint32_t guest_address; + uint32_t width; + uint32_t height; + TextureFormat format; + GLuint handle; + }; + SamplerEntry* LookupOrInsertSampler(const SamplerInfo& sampler_info, uint64_t opt_hash = 0); void EvictSampler(SamplerEntry* entry); TextureEntry* LookupOrInsertTexture(const TextureInfo& texture_info, uint64_t opt_hash = 0); + TextureEntry* LookupAddress(uint32_t guest_address, uint32_t width, + uint32_t height, TextureFormat format); void EvictTexture(TextureEntry* entry); bool UploadTexture2D(GLuint texture, const TextureInfo& texture_info); @@ -73,6 +86,8 @@ class TextureCache { std::unordered_map sampler_entries_; std::unordered_map texture_entries_; + std::vector read_buffer_textures_; + std::mutex invalidated_textures_mutex_; std::vector* invalidated_textures_; std::vector invalidated_textures_sets_[2]; diff --git a/src/xenia/gpu/texture_info.h b/src/xenia/gpu/texture_info.h index 43f654b11..38f422591 100644 --- a/src/xenia/gpu/texture_info.h +++ b/src/xenia/gpu/texture_info.h @@ -80,10 +80,60 @@ enum class TextureFormat : uint32_t { k_DXT5A = 59, k_CTX1 = 60, k_DXT3A_AS_1_1_1_1 = 61, + k_2_10_10_10_FLOAT = 62, kUnknown = 0xFFFFFFFFu, }; +inline TextureFormat ColorFormatToTextureFormat( + xenos::ColorFormat color_format) { + return static_cast(color_format); +} + +inline TextureFormat ColorRenderTargetToTextureFormat( + xenos::ColorRenderTargetFormat color_format) { + switch (color_format) { + case xenos::ColorRenderTargetFormat::k_8_8_8_8: + return TextureFormat::k_8_8_8_8; + case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: + return TextureFormat::k_8_8_8_8; + case xenos::ColorRenderTargetFormat::k_2_10_10_10: + return TextureFormat::k_2_10_10_10; + case xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT: + return TextureFormat::k_2_10_10_10_FLOAT; + case xenos::ColorRenderTargetFormat::k_16_16: + return TextureFormat::k_16_16; + case xenos::ColorRenderTargetFormat::k_16_16_16_16: + return TextureFormat::k_16_16_16_16; + case xenos::ColorRenderTargetFormat::k_16_16_FLOAT: + return TextureFormat::k_16_16_FLOAT; + case xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT: + return TextureFormat::k_16_16_16_16_FLOAT; + case xenos::ColorRenderTargetFormat::k_2_10_10_10_unknown: + return TextureFormat::k_2_10_10_10; + case xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown: + return TextureFormat::k_2_10_10_10_FLOAT; + case xenos::ColorRenderTargetFormat::k_32_FLOAT: + return TextureFormat::k_32_FLOAT; + default: + assert_unhandled_case(color_format); + return TextureFormat::kUnknown; + } +} + +inline TextureFormat DepthRenderTargetToTextureFormat( + xenos::DepthRenderTargetFormat depth_format) { + switch (depth_format) { + case xenos::DepthRenderTargetFormat::kD24S8: + return TextureFormat::k_24_8; + case xenos::DepthRenderTargetFormat::kD24FS8: + return TextureFormat::k_24_8_FLOAT; + default: + assert_unhandled_case(depth_format); + return TextureFormat::kUnknown; + } +} + enum class FormatType { kUncompressed, kCompressed,