mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Merge pull request #2414 from yuriks/texture-decode
Texture decoding cleanups
This commit is contained in:
		
						commit
						18c981b996
					
				
					 12 changed files with 482 additions and 318 deletions
				
			
		|  | @ -20,13 +20,14 @@ | ||||||
| #include "video_core/debug_utils/debug_utils.h" | #include "video_core/debug_utils/debug_utils.h" | ||||||
| #include "video_core/pica.h" | #include "video_core/pica.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
|  | #include "video_core/texture/texture_decode.h" | ||||||
| 
 | 
 | ||||||
| namespace { | namespace { | ||||||
| QImage LoadTexture(const u8* src, const Pica::DebugUtils::TextureInfo& info) { | QImage LoadTexture(const u8* src, const Pica::Texture::TextureInfo& info) { | ||||||
|     QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); |     QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); | ||||||
|     for (int y = 0; y < info.height; ++y) { |     for (int y = 0; y < info.height; ++y) { | ||||||
|         for (int x = 0; x < info.width; ++x) { |         for (int x = 0; x < info.width; ++x) { | ||||||
|             Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info, true); |             Math::Vec4<u8> color = Pica::Texture::LookupTexture(src, x, y, info, true); | ||||||
|             decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); |             decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -36,9 +37,10 @@ QImage LoadTexture(const u8* src, const Pica::DebugUtils::TextureInfo& info) { | ||||||
| 
 | 
 | ||||||
| class TextureInfoWidget : public QWidget { | class TextureInfoWidget : public QWidget { | ||||||
| public: | public: | ||||||
|     TextureInfoWidget(const u8* src, const Pica::DebugUtils::TextureInfo& info, |     TextureInfoWidget(const u8* src, const Pica::Texture::TextureInfo& info, | ||||||
|                       QWidget* parent = nullptr) |                       QWidget* parent = nullptr) | ||||||
|         : QWidget(parent) { |         : QWidget(parent) { | ||||||
|  | 
 | ||||||
|         QLabel* image_widget = new QLabel; |         QLabel* image_widget = new QLabel; | ||||||
|         QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); |         QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info)); | ||||||
|         image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation); |         image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation); | ||||||
|  | @ -160,7 +162,7 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { | ||||||
|         const auto config = texture.config; |         const auto config = texture.config; | ||||||
|         const auto format = texture.format; |         const auto format = texture.format; | ||||||
| 
 | 
 | ||||||
|         const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); |         const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format); | ||||||
|         const u8* src = Memory::GetPhysicalPointer(config.GetPhysicalAddress()); |         const u8* src = Memory::GetPhysicalPointer(config.GetPhysicalAddress()); | ||||||
|         new_info_widget = new TextureInfoWidget(src, info); |         new_info_widget = new TextureInfoWidget(src, info); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "video_core/pica.h" | #include "video_core/pica.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
|  | #include "video_core/texture/texture_decode.h" | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
| 
 | 
 | ||||||
| SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) | SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) | ||||||
|  | @ -512,7 +513,7 @@ void GraphicsSurfaceWidget::OnUpdate() { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const auto texture = Pica::g_state.regs.GetTextures()[texture_index]; |         const auto texture = Pica::g_state.regs.GetTextures()[texture_index]; | ||||||
|         auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format); |         auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, texture.format); | ||||||
| 
 | 
 | ||||||
|         surface_address = info.physical_address; |         surface_address = info.physical_address; | ||||||
|         surface_width = info.width; |         surface_width = info.width; | ||||||
|  | @ -567,28 +568,27 @@ void GraphicsSurfaceWidget::OnUpdate() { | ||||||
| 
 | 
 | ||||||
|     surface_picture_label->show(); |     surface_picture_label->show(); | ||||||
| 
 | 
 | ||||||
|     unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format); |  | ||||||
|     unsigned stride = nibbles_per_pixel * surface_width / 2; |  | ||||||
| 
 |  | ||||||
|     // We handle depth formats here because DebugUtils only supports TextureFormats
 |  | ||||||
|     if (surface_format <= Format::MaxTextureFormat) { |     if (surface_format <= Format::MaxTextureFormat) { | ||||||
| 
 |  | ||||||
|         // Generate a virtual texture
 |         // Generate a virtual texture
 | ||||||
|         Pica::DebugUtils::TextureInfo info; |         Pica::Texture::TextureInfo info; | ||||||
|         info.physical_address = surface_address; |         info.physical_address = surface_address; | ||||||
|         info.width = surface_width; |         info.width = surface_width; | ||||||
|         info.height = surface_height; |         info.height = surface_height; | ||||||
|         info.format = static_cast<Pica::Regs::TextureFormat>(surface_format); |         info.format = static_cast<Pica::Regs::TextureFormat>(surface_format); | ||||||
|         info.stride = stride; |         info.SetDefaultStride(); | ||||||
| 
 | 
 | ||||||
|         for (unsigned int y = 0; y < surface_height; ++y) { |         for (unsigned int y = 0; y < surface_height; ++y) { | ||||||
|             for (unsigned int x = 0; x < surface_width; ++x) { |             for (unsigned int x = 0; x < surface_width; ++x) { | ||||||
|                 Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(buffer, x, y, info, true); |                 Math::Vec4<u8> color = Pica::Texture::LookupTexture(buffer, x, y, info, true); | ||||||
|                 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); |                 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|     } else { |     } else { | ||||||
|  |         // We handle depth formats here because DebugUtils only supports TextureFormats
 | ||||||
|  | 
 | ||||||
|  |         // TODO(yuriks): Convert to newer tile-based addressing
 | ||||||
|  |         unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format); | ||||||
|  |         unsigned stride = nibbles_per_pixel * surface_width / 2; | ||||||
| 
 | 
 | ||||||
|         ASSERT_MSG(nibbles_per_pixel >= 2, |         ASSERT_MSG(nibbles_per_pixel >= 2, | ||||||
|                    "Depth decoder only supports formats with at least one byte per pixel"); |                    "Depth decoder only supports formats with at least one byte per pixel"); | ||||||
|  |  | ||||||
|  | @ -1,36 +1,30 @@ | ||||||
| set(SRCS | set(SRCS | ||||||
|  |             clipper.cpp | ||||||
|  |             command_processor.cpp | ||||||
|  |             debug_utils/debug_utils.cpp | ||||||
|  |             pica.cpp | ||||||
|  |             primitive_assembly.cpp | ||||||
|  |             rasterizer.cpp | ||||||
|  |             renderer_base.cpp | ||||||
|             renderer_opengl/gl_rasterizer.cpp |             renderer_opengl/gl_rasterizer.cpp | ||||||
|             renderer_opengl/gl_rasterizer_cache.cpp |             renderer_opengl/gl_rasterizer_cache.cpp | ||||||
|             renderer_opengl/gl_shader_gen.cpp |             renderer_opengl/gl_shader_gen.cpp | ||||||
|             renderer_opengl/gl_shader_util.cpp |             renderer_opengl/gl_shader_util.cpp | ||||||
|             renderer_opengl/gl_state.cpp |             renderer_opengl/gl_state.cpp | ||||||
|             renderer_opengl/renderer_opengl.cpp |             renderer_opengl/renderer_opengl.cpp | ||||||
|             debug_utils/debug_utils.cpp |  | ||||||
|             clipper.cpp |  | ||||||
|             command_processor.cpp |  | ||||||
|             pica.cpp |  | ||||||
|             primitive_assembly.cpp |  | ||||||
|             rasterizer.cpp |  | ||||||
|             renderer_base.cpp |  | ||||||
|             shader/shader.cpp |             shader/shader.cpp | ||||||
|             shader/shader_interpreter.cpp |             shader/shader_interpreter.cpp | ||||||
|             swrasterizer.cpp |             swrasterizer.cpp | ||||||
|  |             texture/etc1.cpp | ||||||
|  |             texture/texture_decode.cpp | ||||||
|             vertex_loader.cpp |             vertex_loader.cpp | ||||||
|             video_core.cpp |             video_core.cpp | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
| set(HEADERS | set(HEADERS | ||||||
|             debug_utils/debug_utils.h |  | ||||||
|             renderer_opengl/gl_rasterizer.h |  | ||||||
|             renderer_opengl/gl_rasterizer_cache.h |  | ||||||
|             renderer_opengl/gl_resource_manager.h |  | ||||||
|             renderer_opengl/gl_shader_gen.h |  | ||||||
|             renderer_opengl/gl_shader_util.h |  | ||||||
|             renderer_opengl/gl_state.h |  | ||||||
|             renderer_opengl/pica_to_gl.h |  | ||||||
|             renderer_opengl/renderer_opengl.h |  | ||||||
|             clipper.h |             clipper.h | ||||||
|             command_processor.h |             command_processor.h | ||||||
|  |             debug_utils/debug_utils.h | ||||||
|             gpu_debugger.h |             gpu_debugger.h | ||||||
|             pica.h |             pica.h | ||||||
|             pica_state.h |             pica_state.h | ||||||
|  | @ -39,10 +33,20 @@ set(HEADERS | ||||||
|             rasterizer.h |             rasterizer.h | ||||||
|             rasterizer_interface.h |             rasterizer_interface.h | ||||||
|             renderer_base.h |             renderer_base.h | ||||||
|  |             renderer_opengl/gl_rasterizer.h | ||||||
|  |             renderer_opengl/gl_rasterizer_cache.h | ||||||
|  |             renderer_opengl/gl_resource_manager.h | ||||||
|  |             renderer_opengl/gl_shader_gen.h | ||||||
|  |             renderer_opengl/gl_shader_util.h | ||||||
|  |             renderer_opengl/gl_state.h | ||||||
|  |             renderer_opengl/pica_to_gl.h | ||||||
|  |             renderer_opengl/renderer_opengl.h | ||||||
|             shader/debug_data.h |             shader/debug_data.h | ||||||
|             shader/shader.h |             shader/shader.h | ||||||
|             shader/shader_interpreter.h |             shader/shader_interpreter.h | ||||||
|             swrasterizer.h |             swrasterizer.h | ||||||
|  |             texture/etc1.h | ||||||
|  |             texture/texture_decode.h | ||||||
|             utils.h |             utils.h | ||||||
|             vertex_loader.h |             vertex_loader.h | ||||||
|             video_core.h |             video_core.h | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ | ||||||
| #include "video_core/rasterizer_interface.h" | #include "video_core/rasterizer_interface.h" | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
| #include "video_core/shader/shader.h" | #include "video_core/shader/shader.h" | ||||||
|  | #include "video_core/texture/texture_decode.h" | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | @ -315,257 +316,6 @@ std::unique_ptr<PicaTrace> FinishPicaTracing() { | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info, |  | ||||||
|                                    bool disable_alpha) { |  | ||||||
|     const unsigned int coarse_x = x & ~7; |  | ||||||
|     const unsigned int coarse_y = y & ~7; |  | ||||||
| 
 |  | ||||||
|     if (info.format != Regs::TextureFormat::ETC1 && info.format != Regs::TextureFormat::ETC1A4) { |  | ||||||
|         // TODO(neobrain): Fix code design to unify vertical block offsets!
 |  | ||||||
|         source += coarse_y * info.stride; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // TODO: Assert that width/height are multiples of block dimensions
 |  | ||||||
| 
 |  | ||||||
|     switch (info.format) { |  | ||||||
|     case Regs::TextureFormat::RGBA8: { |  | ||||||
|         auto res = Color::DecodeRGBA8(source + VideoCore::GetMortonOffset(x, y, 4)); |  | ||||||
|         return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::RGB8: { |  | ||||||
|         auto res = Color::DecodeRGB8(source + VideoCore::GetMortonOffset(x, y, 3)); |  | ||||||
|         return {res.r(), res.g(), res.b(), 255}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::RGB5A1: { |  | ||||||
|         auto res = Color::DecodeRGB5A1(source + VideoCore::GetMortonOffset(x, y, 2)); |  | ||||||
|         return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::RGB565: { |  | ||||||
|         auto res = Color::DecodeRGB565(source + VideoCore::GetMortonOffset(x, y, 2)); |  | ||||||
|         return {res.r(), res.g(), res.b(), 255}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::RGBA4: { |  | ||||||
|         auto res = Color::DecodeRGBA4(source + VideoCore::GetMortonOffset(x, y, 2)); |  | ||||||
|         return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::IA8: { |  | ||||||
|         const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 2); |  | ||||||
| 
 |  | ||||||
|         if (disable_alpha) { |  | ||||||
|             // Show intensity as red, alpha as green
 |  | ||||||
|             return {source_ptr[1], source_ptr[0], 0, 255}; |  | ||||||
|         } else { |  | ||||||
|             return {source_ptr[1], source_ptr[1], source_ptr[1], source_ptr[0]}; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::RG8: { |  | ||||||
|         auto res = Color::DecodeRG8(source + VideoCore::GetMortonOffset(x, y, 2)); |  | ||||||
|         return {res.r(), res.g(), 0, 255}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::I8: { |  | ||||||
|         const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1); |  | ||||||
|         return {*source_ptr, *source_ptr, *source_ptr, 255}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::A8: { |  | ||||||
|         const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1); |  | ||||||
| 
 |  | ||||||
|         if (disable_alpha) { |  | ||||||
|             return {*source_ptr, *source_ptr, *source_ptr, 255}; |  | ||||||
|         } else { |  | ||||||
|             return {0, 0, 0, *source_ptr}; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::IA4: { |  | ||||||
|         const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1); |  | ||||||
| 
 |  | ||||||
|         u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4); |  | ||||||
|         u8 a = Color::Convert4To8((*source_ptr) & 0xF); |  | ||||||
| 
 |  | ||||||
|         if (disable_alpha) { |  | ||||||
|             // Show intensity as red, alpha as green
 |  | ||||||
|             return {i, a, 0, 255}; |  | ||||||
|         } else { |  | ||||||
|             return {i, i, i, a}; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::I4: { |  | ||||||
|         u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1); |  | ||||||
|         const u8* source_ptr = source + morton_offset / 2; |  | ||||||
| 
 |  | ||||||
|         u8 i = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF); |  | ||||||
|         i = Color::Convert4To8(i); |  | ||||||
| 
 |  | ||||||
|         return {i, i, i, 255}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::A4: { |  | ||||||
|         u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1); |  | ||||||
|         const u8* source_ptr = source + morton_offset / 2; |  | ||||||
| 
 |  | ||||||
|         u8 a = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF); |  | ||||||
|         a = Color::Convert4To8(a); |  | ||||||
| 
 |  | ||||||
|         if (disable_alpha) { |  | ||||||
|             return {a, a, a, 255}; |  | ||||||
|         } else { |  | ||||||
|             return {0, 0, 0, a}; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     case Regs::TextureFormat::ETC1: |  | ||||||
|     case Regs::TextureFormat::ETC1A4: { |  | ||||||
|         bool has_alpha = (info.format == Regs::TextureFormat::ETC1A4); |  | ||||||
| 
 |  | ||||||
|         // ETC1 further subdivides each 8x8 tile into four 4x4 subtiles
 |  | ||||||
|         const int subtile_width = 4; |  | ||||||
|         const int subtile_height = 4; |  | ||||||
| 
 |  | ||||||
|         int subtile_index = ((x / subtile_width) & 1) + 2 * ((y / subtile_height) & 1); |  | ||||||
|         unsigned subtile_bytes = has_alpha ? 2 : 1; // TODO: Name...
 |  | ||||||
| 
 |  | ||||||
|         const u64* source_ptr = (const u64*)(source + coarse_x * subtile_bytes * 4 + |  | ||||||
|                                              coarse_y * subtile_bytes * 4 * (info.width / 8) + |  | ||||||
|                                              subtile_index * subtile_bytes * 8); |  | ||||||
|         u64 alpha = 0xFFFFFFFFFFFFFFFF; |  | ||||||
|         if (has_alpha) { |  | ||||||
|             alpha = *source_ptr; |  | ||||||
|             source_ptr++; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         union ETC1Tile { |  | ||||||
|             // Each of these two is a collection of 16 bits (one per lookup value)
 |  | ||||||
|             BitField<0, 16, u64> table_subindexes; |  | ||||||
|             BitField<16, 16, u64> negation_flags; |  | ||||||
| 
 |  | ||||||
|             unsigned GetTableSubIndex(unsigned index) const { |  | ||||||
|                 return (table_subindexes >> index) & 1; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             bool GetNegationFlag(unsigned index) const { |  | ||||||
|                 return ((negation_flags >> index) & 1) == 1; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             BitField<32, 1, u64> flip; |  | ||||||
|             BitField<33, 1, u64> differential_mode; |  | ||||||
| 
 |  | ||||||
|             BitField<34, 3, u64> table_index_2; |  | ||||||
|             BitField<37, 3, u64> table_index_1; |  | ||||||
| 
 |  | ||||||
|             union { |  | ||||||
|                 // delta value + base value
 |  | ||||||
|                 BitField<40, 3, s64> db; |  | ||||||
|                 BitField<43, 5, u64> b; |  | ||||||
| 
 |  | ||||||
|                 BitField<48, 3, s64> dg; |  | ||||||
|                 BitField<51, 5, u64> g; |  | ||||||
| 
 |  | ||||||
|                 BitField<56, 3, s64> dr; |  | ||||||
|                 BitField<59, 5, u64> r; |  | ||||||
|             } differential; |  | ||||||
| 
 |  | ||||||
|             union { |  | ||||||
|                 BitField<40, 4, u64> b2; |  | ||||||
|                 BitField<44, 4, u64> b1; |  | ||||||
| 
 |  | ||||||
|                 BitField<48, 4, u64> g2; |  | ||||||
|                 BitField<52, 4, u64> g1; |  | ||||||
| 
 |  | ||||||
|                 BitField<56, 4, u64> r2; |  | ||||||
|                 BitField<60, 4, u64> r1; |  | ||||||
|             } separate; |  | ||||||
| 
 |  | ||||||
|             const Math::Vec3<u8> GetRGB(int x, int y) const { |  | ||||||
|                 int texel = 4 * x + y; |  | ||||||
| 
 |  | ||||||
|                 if (flip) |  | ||||||
|                     std::swap(x, y); |  | ||||||
| 
 |  | ||||||
|                 // Lookup base value
 |  | ||||||
|                 Math::Vec3<int> ret; |  | ||||||
|                 if (differential_mode) { |  | ||||||
|                     ret.r() = static_cast<int>(differential.r); |  | ||||||
|                     ret.g() = static_cast<int>(differential.g); |  | ||||||
|                     ret.b() = static_cast<int>(differential.b); |  | ||||||
|                     if (x >= 2) { |  | ||||||
|                         ret.r() += static_cast<int>(differential.dr); |  | ||||||
|                         ret.g() += static_cast<int>(differential.dg); |  | ||||||
|                         ret.b() += static_cast<int>(differential.db); |  | ||||||
|                     } |  | ||||||
|                     ret.r() = Color::Convert5To8(ret.r()); |  | ||||||
|                     ret.g() = Color::Convert5To8(ret.g()); |  | ||||||
|                     ret.b() = Color::Convert5To8(ret.b()); |  | ||||||
|                 } else { |  | ||||||
|                     if (x < 2) { |  | ||||||
|                         ret.r() = Color::Convert4To8(static_cast<u8>(separate.r1)); |  | ||||||
|                         ret.g() = Color::Convert4To8(static_cast<u8>(separate.g1)); |  | ||||||
|                         ret.b() = Color::Convert4To8(static_cast<u8>(separate.b1)); |  | ||||||
|                     } else { |  | ||||||
|                         ret.r() = Color::Convert4To8(static_cast<u8>(separate.r2)); |  | ||||||
|                         ret.g() = Color::Convert4To8(static_cast<u8>(separate.g2)); |  | ||||||
|                         ret.b() = Color::Convert4To8(static_cast<u8>(separate.b2)); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // Add modifier
 |  | ||||||
|                 unsigned table_index = |  | ||||||
|                     static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value()); |  | ||||||
| 
 |  | ||||||
|                 static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{ |  | ||||||
|                     {{2, 8}}, |  | ||||||
|                     {{5, 17}}, |  | ||||||
|                     {{9, 29}}, |  | ||||||
|                     {{13, 42}}, |  | ||||||
|                     {{18, 60}}, |  | ||||||
|                     {{24, 80}}, |  | ||||||
|                     {{33, 106}}, |  | ||||||
|                     {{47, 183}}, |  | ||||||
|                 }}; |  | ||||||
| 
 |  | ||||||
|                 int modifier = etc1_modifier_table.at(table_index).at(GetTableSubIndex(texel)); |  | ||||||
|                 if (GetNegationFlag(texel)) |  | ||||||
|                     modifier *= -1; |  | ||||||
| 
 |  | ||||||
|                 ret.r() = MathUtil::Clamp(ret.r() + modifier, 0, 255); |  | ||||||
|                 ret.g() = MathUtil::Clamp(ret.g() + modifier, 0, 255); |  | ||||||
|                 ret.b() = MathUtil::Clamp(ret.b() + modifier, 0, 255); |  | ||||||
| 
 |  | ||||||
|                 return ret.Cast<u8>(); |  | ||||||
|             } |  | ||||||
|         } const* etc1_tile = reinterpret_cast<const ETC1Tile*>(source_ptr); |  | ||||||
| 
 |  | ||||||
|         alpha >>= 4 * ((x & 3) * 4 + (y & 3)); |  | ||||||
|         return Math::MakeVec(etc1_tile->GetRGB(x & 3, y & 3), |  | ||||||
|                              disable_alpha ? (u8)255 : Color::Convert4To8(alpha & 0xF)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     default: |  | ||||||
|         LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format); |  | ||||||
|         DEBUG_ASSERT(false); |  | ||||||
|         return {}; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, |  | ||||||
|                                           const Regs::TextureFormat& format) { |  | ||||||
|     TextureInfo info; |  | ||||||
|     info.physical_address = config.GetPhysicalAddress(); |  | ||||||
|     info.width = config.width; |  | ||||||
|     info.height = config.height; |  | ||||||
|     info.format = format; |  | ||||||
|     info.stride = Pica::Regs::NibblesPerPixel(info.format) * info.width / 2; |  | ||||||
|     return info; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #ifdef HAVE_PNG | #ifdef HAVE_PNG | ||||||
| // Adapter functions to libpng to write/flush to File::IOFile instances.
 | // Adapter functions to libpng to write/flush to File::IOFile instances.
 | ||||||
| static void WriteIOFile(png_structp png_ptr, png_bytep data, png_size_t length) { | static void WriteIOFile(png_structp png_ptr, png_bytep data, png_size_t length) { | ||||||
|  | @ -642,12 +392,12 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { | ||||||
|     buf = new u8[row_stride * texture_config.height]; |     buf = new u8[row_stride * texture_config.height]; | ||||||
|     for (unsigned y = 0; y < texture_config.height; ++y) { |     for (unsigned y = 0; y < texture_config.height; ++y) { | ||||||
|         for (unsigned x = 0; x < texture_config.width; ++x) { |         for (unsigned x = 0; x < texture_config.width; ++x) { | ||||||
|             TextureInfo info; |             Pica::Texture::TextureInfo info; | ||||||
|             info.width = texture_config.width; |             info.width = texture_config.width; | ||||||
|             info.height = texture_config.height; |             info.height = texture_config.height; | ||||||
|             info.stride = row_stride; |             info.stride = row_stride; | ||||||
|             info.format = g_state.regs.texture0_format; |             info.format = g_state.regs.texture0_format; | ||||||
|             Math::Vec4<u8> texture_color = LookupTexture(data, x, y, info); |             Math::Vec4<u8> texture_color = Pica::Texture::LookupTexture(data, x, y, info); | ||||||
|             buf[3 * x + y * row_stride] = texture_color.r(); |             buf[3 * x + y * row_stride] = texture_color.r(); | ||||||
|             buf[3 * x + y * row_stride + 1] = texture_color.g(); |             buf[3 * x + y * row_stride + 1] = texture_color.g(); | ||||||
|             buf[3 * x + y * row_stride + 2] = texture_color.b(); |             buf[3 * x + y * row_stride + 2] = texture_color.b(); | ||||||
|  |  | ||||||
|  | @ -205,31 +205,6 @@ inline bool IsPicaTracing() { | ||||||
| void OnPicaRegWrite(PicaTrace::Write write); | void OnPicaRegWrite(PicaTrace::Write write); | ||||||
| std::unique_ptr<PicaTrace> FinishPicaTracing(); | std::unique_ptr<PicaTrace> FinishPicaTracing(); | ||||||
| 
 | 
 | ||||||
| struct TextureInfo { |  | ||||||
|     PAddr physical_address; |  | ||||||
|     int width; |  | ||||||
|     int height; |  | ||||||
|     int stride; |  | ||||||
|     Pica::Regs::TextureFormat format; |  | ||||||
| 
 |  | ||||||
|     static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config, |  | ||||||
|                                         const Pica::Regs::TextureFormat& format); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Lookup texel located at the given coordinates and return an RGBA vector of its color. |  | ||||||
|  * @param source Source pointer to read data from |  | ||||||
|  * @param s,t Texture coordinates to read from |  | ||||||
|  * @param info TextureInfo object describing the texture setup |  | ||||||
|  * @param disable_alpha This is used for debug widgets which use this method to display textures |  | ||||||
|  * without providing a good way to visualize alpha by themselves. If true, this will return 255 for |  | ||||||
|  * the alpha component, and either drop the information entirely or store it in an "unused" color |  | ||||||
|  * channel. |  | ||||||
|  * @todo Eventually we should get rid of the disable_alpha parameter. |  | ||||||
|  */ |  | ||||||
| const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info, |  | ||||||
|                                    bool disable_alpha = false); |  | ||||||
| 
 |  | ||||||
| void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); | ||||||
| 
 | 
 | ||||||
| std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage); | std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage); | ||||||
|  |  | ||||||
|  | @ -275,8 +275,11 @@ struct Regs { | ||||||
|         case TextureFormat::I8: |         case TextureFormat::I8: | ||||||
|         case TextureFormat::A8: |         case TextureFormat::A8: | ||||||
|         case TextureFormat::IA4: |         case TextureFormat::IA4: | ||||||
|         default: // placeholder for yet unknown formats
 |  | ||||||
|             return 2; |             return 2; | ||||||
|  | 
 | ||||||
|  |         default: // placeholder for yet unknown formats
 | ||||||
|  |             UNIMPLEMENTED(); | ||||||
|  |             return 0; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ | ||||||
| #include "video_core/pica_types.h" | #include "video_core/pica_types.h" | ||||||
| #include "video_core/rasterizer.h" | #include "video_core/rasterizer.h" | ||||||
| #include "video_core/shader/shader.h" | #include "video_core/shader/shader.h" | ||||||
|  | #include "video_core/texture/texture_decode.h" | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
| 
 | 
 | ||||||
| namespace Pica { | namespace Pica { | ||||||
|  | @ -579,10 +580,10 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader | ||||||
|                     u8* texture_data = |                     u8* texture_data = | ||||||
|                         Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); |                         Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); | ||||||
|                     auto info = |                     auto info = | ||||||
|                         DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format); |                         Texture::TextureInfo::FromPicaRegister(texture.config, texture.format); | ||||||
| 
 | 
 | ||||||
|                     // TODO: Apply the min and mag filters to the texture
 |                     // TODO: Apply the min and mag filters to the texture
 | ||||||
|                     texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info); |                     texture_color[i] = Texture::LookupTexture(texture_data, s, t, info); | ||||||
| #if PICA_DUMP_TEXTURES | #if PICA_DUMP_TEXTURES | ||||||
|                     DebugUtils::DumpTexture(texture.config, texture_data); |                     DebugUtils::DumpTexture(texture.config, texture_data); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -17,10 +17,10 @@ | ||||||
| #include "common/vector_math.h" | #include "common/vector_math.h" | ||||||
| #include "core/frontend/emu_window.h" | #include "core/frontend/emu_window.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "video_core/debug_utils/debug_utils.h" |  | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
| #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | ||||||
| #include "video_core/renderer_opengl/gl_state.h" | #include "video_core/renderer_opengl/gl_state.h" | ||||||
|  | #include "video_core/texture/texture_decode.h" | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | @ -339,17 +339,16 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo | ||||||
| 
 | 
 | ||||||
|                 std::vector<Math::Vec4<u8>> tex_buffer(params.width * params.height); |                 std::vector<Math::Vec4<u8>> tex_buffer(params.width * params.height); | ||||||
| 
 | 
 | ||||||
|                 Pica::DebugUtils::TextureInfo tex_info; |                 Pica::Texture::TextureInfo tex_info; | ||||||
|                 tex_info.width = params.width; |                 tex_info.width = params.width; | ||||||
|                 tex_info.height = params.height; |                 tex_info.height = params.height; | ||||||
|                 tex_info.stride = |  | ||||||
|                     params.width * CachedSurface::GetFormatBpp(params.pixel_format) / 8; |  | ||||||
|                 tex_info.format = (Pica::Regs::TextureFormat)params.pixel_format; |                 tex_info.format = (Pica::Regs::TextureFormat)params.pixel_format; | ||||||
|  |                 tex_info.SetDefaultStride(); | ||||||
|                 tex_info.physical_address = params.addr; |                 tex_info.physical_address = params.addr; | ||||||
| 
 | 
 | ||||||
|                 for (unsigned y = 0; y < params.height; ++y) { |                 for (unsigned y = 0; y < params.height; ++y) { | ||||||
|                     for (unsigned x = 0; x < params.width; ++x) { |                     for (unsigned x = 0; x < params.width; ++x) { | ||||||
|                         tex_buffer[x + params.width * y] = Pica::DebugUtils::LookupTexture( |                         tex_buffer[x + params.width * y] = Pica::Texture::LookupTexture( | ||||||
|                             texture_src_data, x, params.height - 1 - y, tex_info); |                             texture_src_data, x, params.height - 1 - y, tex_info); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  | @ -512,8 +511,9 @@ CachedSurface* RasterizerCacheOpenGL::GetSurfaceRect(const CachedSurface& params | ||||||
| 
 | 
 | ||||||
| CachedSurface* RasterizerCacheOpenGL::GetTextureSurface( | CachedSurface* RasterizerCacheOpenGL::GetTextureSurface( | ||||||
|     const Pica::Regs::FullTextureConfig& config) { |     const Pica::Regs::FullTextureConfig& config) { | ||||||
|     Pica::DebugUtils::TextureInfo info = | 
 | ||||||
|         Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format); |     Pica::Texture::TextureInfo info = | ||||||
|  |         Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); | ||||||
| 
 | 
 | ||||||
|     CachedSurface params; |     CachedSurface params; | ||||||
|     params.addr = info.physical_address; |     params.addr = info.physical_address; | ||||||
|  |  | ||||||
							
								
								
									
										124
									
								
								src/video_core/texture/etc1.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/video_core/texture/etc1.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,124 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "common/color.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/math_util.h" | ||||||
|  | #include "common/vector_math.h" | ||||||
|  | #include "video_core/texture/etc1.h" | ||||||
|  | 
 | ||||||
|  | namespace Pica { | ||||||
|  | namespace Texture { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | constexpr std::array<u8[2], 8> etc1_modifier_table = {{ | ||||||
|  |     {2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183}, | ||||||
|  | }}; | ||||||
|  | 
 | ||||||
|  | union ETC1Tile { | ||||||
|  |     u64 raw; | ||||||
|  | 
 | ||||||
|  |     // Each of these two is a collection of 16 bits (one per lookup value)
 | ||||||
|  |     BitField<0, 16, u64> table_subindexes; | ||||||
|  |     BitField<16, 16, u64> negation_flags; | ||||||
|  | 
 | ||||||
|  |     unsigned GetTableSubIndex(unsigned index) const { | ||||||
|  |         return (table_subindexes >> index) & 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool GetNegationFlag(unsigned index) const { | ||||||
|  |         return ((negation_flags >> index) & 1) == 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     BitField<32, 1, u64> flip; | ||||||
|  |     BitField<33, 1, u64> differential_mode; | ||||||
|  | 
 | ||||||
|  |     BitField<34, 3, u64> table_index_2; | ||||||
|  |     BitField<37, 3, u64> table_index_1; | ||||||
|  | 
 | ||||||
|  |     union { | ||||||
|  |         // delta value + base value
 | ||||||
|  |         BitField<40, 3, s64> db; | ||||||
|  |         BitField<43, 5, u64> b; | ||||||
|  | 
 | ||||||
|  |         BitField<48, 3, s64> dg; | ||||||
|  |         BitField<51, 5, u64> g; | ||||||
|  | 
 | ||||||
|  |         BitField<56, 3, s64> dr; | ||||||
|  |         BitField<59, 5, u64> r; | ||||||
|  |     } differential; | ||||||
|  | 
 | ||||||
|  |     union { | ||||||
|  |         BitField<40, 4, u64> b2; | ||||||
|  |         BitField<44, 4, u64> b1; | ||||||
|  | 
 | ||||||
|  |         BitField<48, 4, u64> g2; | ||||||
|  |         BitField<52, 4, u64> g1; | ||||||
|  | 
 | ||||||
|  |         BitField<56, 4, u64> r2; | ||||||
|  |         BitField<60, 4, u64> r1; | ||||||
|  |     } separate; | ||||||
|  | 
 | ||||||
|  |     const Math::Vec3<u8> GetRGB(unsigned int x, unsigned int y) const { | ||||||
|  |         int texel = 4 * x + y; | ||||||
|  | 
 | ||||||
|  |         if (flip) | ||||||
|  |             std::swap(x, y); | ||||||
|  | 
 | ||||||
|  |         // Lookup base value
 | ||||||
|  |         Math::Vec3<int> ret; | ||||||
|  |         if (differential_mode) { | ||||||
|  |             ret.r() = static_cast<int>(differential.r); | ||||||
|  |             ret.g() = static_cast<int>(differential.g); | ||||||
|  |             ret.b() = static_cast<int>(differential.b); | ||||||
|  |             if (x >= 2) { | ||||||
|  |                 ret.r() += static_cast<int>(differential.dr); | ||||||
|  |                 ret.g() += static_cast<int>(differential.dg); | ||||||
|  |                 ret.b() += static_cast<int>(differential.db); | ||||||
|  |             } | ||||||
|  |             ret.r() = Color::Convert5To8(ret.r()); | ||||||
|  |             ret.g() = Color::Convert5To8(ret.g()); | ||||||
|  |             ret.b() = Color::Convert5To8(ret.b()); | ||||||
|  |         } else { | ||||||
|  |             if (x < 2) { | ||||||
|  |                 ret.r() = Color::Convert4To8(static_cast<u8>(separate.r1)); | ||||||
|  |                 ret.g() = Color::Convert4To8(static_cast<u8>(separate.g1)); | ||||||
|  |                 ret.b() = Color::Convert4To8(static_cast<u8>(separate.b1)); | ||||||
|  |             } else { | ||||||
|  |                 ret.r() = Color::Convert4To8(static_cast<u8>(separate.r2)); | ||||||
|  |                 ret.g() = Color::Convert4To8(static_cast<u8>(separate.g2)); | ||||||
|  |                 ret.b() = Color::Convert4To8(static_cast<u8>(separate.b2)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Add modifier
 | ||||||
|  |         unsigned table_index = | ||||||
|  |             static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value()); | ||||||
|  | 
 | ||||||
|  |         int modifier = etc1_modifier_table[table_index][GetTableSubIndex(texel)]; | ||||||
|  |         if (GetNegationFlag(texel)) | ||||||
|  |             modifier *= -1; | ||||||
|  | 
 | ||||||
|  |         ret.r() = MathUtil::Clamp(ret.r() + modifier, 0, 255); | ||||||
|  |         ret.g() = MathUtil::Clamp(ret.g() + modifier, 0, 255); | ||||||
|  |         ret.b() = MathUtil::Clamp(ret.b() + modifier, 0, 255); | ||||||
|  | 
 | ||||||
|  |         return ret.Cast<u8>(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // anonymous namespace
 | ||||||
|  | 
 | ||||||
|  | Math::Vec3<u8> SampleETC1Subtile(u64 value, unsigned int x, unsigned int y) { | ||||||
|  |     ETC1Tile tile{value}; | ||||||
|  |     return tile.GetRGB(x, y); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Texture
 | ||||||
|  | } // namespace Pica
 | ||||||
							
								
								
									
										16
									
								
								src/video_core/texture/etc1.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/video_core/texture/etc1.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/vector_math.h" | ||||||
|  | 
 | ||||||
|  | namespace Pica { | ||||||
|  | namespace Texture { | ||||||
|  | 
 | ||||||
|  | Math::Vec3<u8> SampleETC1Subtile(u64 value, unsigned int x, unsigned int y); | ||||||
|  | 
 | ||||||
|  | } // namespace Texture
 | ||||||
|  | } // namespace Pica
 | ||||||
							
								
								
									
										229
									
								
								src/video_core/texture/texture_decode.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/video_core/texture/texture_decode.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,229 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/color.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "common/math_util.h" | ||||||
|  | #include "common/swap.h" | ||||||
|  | #include "common/vector_math.h" | ||||||
|  | #include "video_core/pica.h" | ||||||
|  | #include "video_core/texture/etc1.h" | ||||||
|  | #include "video_core/texture/texture_decode.h" | ||||||
|  | #include "video_core/utils.h" | ||||||
|  | 
 | ||||||
|  | using TextureFormat = Pica::Regs::TextureFormat; | ||||||
|  | 
 | ||||||
|  | namespace Pica { | ||||||
|  | namespace Texture { | ||||||
|  | 
 | ||||||
|  | constexpr size_t TILE_SIZE = 8 * 8; | ||||||
|  | constexpr size_t ETC1_SUBTILES = 2 * 2; | ||||||
|  | 
 | ||||||
|  | size_t CalculateTileSize(TextureFormat format) { | ||||||
|  |     switch (format) { | ||||||
|  |     case TextureFormat::RGBA8: | ||||||
|  |         return 4 * TILE_SIZE; | ||||||
|  | 
 | ||||||
|  |     case TextureFormat::RGB8: | ||||||
|  |         return 3 * TILE_SIZE; | ||||||
|  | 
 | ||||||
|  |     case TextureFormat::RGB5A1: | ||||||
|  |     case TextureFormat::RGB565: | ||||||
|  |     case TextureFormat::RGBA4: | ||||||
|  |     case TextureFormat::IA8: | ||||||
|  |     case TextureFormat::RG8: | ||||||
|  |         return 2 * TILE_SIZE; | ||||||
|  | 
 | ||||||
|  |     case TextureFormat::I8: | ||||||
|  |     case TextureFormat::A8: | ||||||
|  |     case TextureFormat::IA4: | ||||||
|  |         return 1 * TILE_SIZE; | ||||||
|  | 
 | ||||||
|  |     case TextureFormat::I4: | ||||||
|  |     case TextureFormat::A4: | ||||||
|  |         return TILE_SIZE / 2; | ||||||
|  | 
 | ||||||
|  |     case TextureFormat::ETC1: | ||||||
|  |         return ETC1_SUBTILES * 8; | ||||||
|  | 
 | ||||||
|  |     case TextureFormat::ETC1A4: | ||||||
|  |         return ETC1_SUBTILES * 16; | ||||||
|  | 
 | ||||||
|  |     default: // placeholder for yet unknown formats
 | ||||||
|  |         UNIMPLEMENTED(); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Math::Vec4<u8> LookupTexture(const u8* source, unsigned int x, unsigned int y, | ||||||
|  |                              const TextureInfo& info, bool disable_alpha) { | ||||||
|  |     // Coordinate in tiles
 | ||||||
|  |     const unsigned int coarse_x = x / 8; | ||||||
|  |     const unsigned int coarse_y = y / 8; | ||||||
|  | 
 | ||||||
|  |     // Coordinate inside the tile
 | ||||||
|  |     const unsigned int fine_x = x % 8; | ||||||
|  |     const unsigned int fine_y = y % 8; | ||||||
|  | 
 | ||||||
|  |     const u8* line = source + coarse_y * info.stride; | ||||||
|  |     const u8* tile = line + coarse_x * CalculateTileSize(info.format); | ||||||
|  |     return LookupTexelInTile(tile, fine_x, fine_y, info, disable_alpha); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Math::Vec4<u8> LookupTexelInTile(const u8* source, unsigned int x, unsigned int y, | ||||||
|  |                                  const TextureInfo& info, bool disable_alpha) { | ||||||
|  |     DEBUG_ASSERT(x < 8); | ||||||
|  |     DEBUG_ASSERT(y < 8); | ||||||
|  | 
 | ||||||
|  |     using VideoCore::MortonInterleave; | ||||||
|  | 
 | ||||||
|  |     switch (info.format) { | ||||||
|  |     case Regs::TextureFormat::RGBA8: { | ||||||
|  |         auto res = Color::DecodeRGBA8(source + MortonInterleave(x, y) * 4); | ||||||
|  |         return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::RGB8: { | ||||||
|  |         auto res = Color::DecodeRGB8(source + MortonInterleave(x, y) * 3); | ||||||
|  |         return {res.r(), res.g(), res.b(), 255}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::RGB5A1: { | ||||||
|  |         auto res = Color::DecodeRGB5A1(source + MortonInterleave(x, y) * 2); | ||||||
|  |         return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::RGB565: { | ||||||
|  |         auto res = Color::DecodeRGB565(source + MortonInterleave(x, y) * 2); | ||||||
|  |         return {res.r(), res.g(), res.b(), 255}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::RGBA4: { | ||||||
|  |         auto res = Color::DecodeRGBA4(source + MortonInterleave(x, y) * 2); | ||||||
|  |         return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::IA8: { | ||||||
|  |         const u8* source_ptr = source + MortonInterleave(x, y) * 2; | ||||||
|  | 
 | ||||||
|  |         if (disable_alpha) { | ||||||
|  |             // Show intensity as red, alpha as green
 | ||||||
|  |             return {source_ptr[1], source_ptr[0], 0, 255}; | ||||||
|  |         } else { | ||||||
|  |             return {source_ptr[1], source_ptr[1], source_ptr[1], source_ptr[0]}; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::RG8: { | ||||||
|  |         auto res = Color::DecodeRG8(source + MortonInterleave(x, y) * 2); | ||||||
|  |         return {res.r(), res.g(), 0, 255}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::I8: { | ||||||
|  |         const u8* source_ptr = source + MortonInterleave(x, y); | ||||||
|  |         return {*source_ptr, *source_ptr, *source_ptr, 255}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::A8: { | ||||||
|  |         const u8* source_ptr = source + MortonInterleave(x, y); | ||||||
|  | 
 | ||||||
|  |         if (disable_alpha) { | ||||||
|  |             return {*source_ptr, *source_ptr, *source_ptr, 255}; | ||||||
|  |         } else { | ||||||
|  |             return {0, 0, 0, *source_ptr}; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::IA4: { | ||||||
|  |         const u8* source_ptr = source + MortonInterleave(x, y); | ||||||
|  | 
 | ||||||
|  |         u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4); | ||||||
|  |         u8 a = Color::Convert4To8((*source_ptr) & 0xF); | ||||||
|  | 
 | ||||||
|  |         if (disable_alpha) { | ||||||
|  |             // Show intensity as red, alpha as green
 | ||||||
|  |             return {i, a, 0, 255}; | ||||||
|  |         } else { | ||||||
|  |             return {i, i, i, a}; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::I4: { | ||||||
|  |         u32 morton_offset = MortonInterleave(x, y); | ||||||
|  |         const u8* source_ptr = source + morton_offset / 2; | ||||||
|  | 
 | ||||||
|  |         u8 i = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF); | ||||||
|  |         i = Color::Convert4To8(i); | ||||||
|  | 
 | ||||||
|  |         return {i, i, i, 255}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::A4: { | ||||||
|  |         u32 morton_offset = MortonInterleave(x, y); | ||||||
|  |         const u8* source_ptr = source + morton_offset / 2; | ||||||
|  | 
 | ||||||
|  |         u8 a = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF); | ||||||
|  |         a = Color::Convert4To8(a); | ||||||
|  | 
 | ||||||
|  |         if (disable_alpha) { | ||||||
|  |             return {a, a, a, 255}; | ||||||
|  |         } else { | ||||||
|  |             return {0, 0, 0, a}; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     case Regs::TextureFormat::ETC1: | ||||||
|  |     case Regs::TextureFormat::ETC1A4: { | ||||||
|  |         bool has_alpha = (info.format == Regs::TextureFormat::ETC1A4); | ||||||
|  |         size_t subtile_size = has_alpha ? 16 : 8; | ||||||
|  | 
 | ||||||
|  |         // ETC1 further subdivides each 8x8 tile into four 4x4 subtiles
 | ||||||
|  |         constexpr unsigned int subtile_width = 4; | ||||||
|  |         constexpr unsigned int subtile_height = 4; | ||||||
|  | 
 | ||||||
|  |         unsigned int subtile_index = (x / subtile_width) + 2 * (y / subtile_height); | ||||||
|  |         x %= subtile_width; | ||||||
|  |         y %= subtile_height; | ||||||
|  | 
 | ||||||
|  |         const u8* subtile_ptr = source + subtile_index * subtile_size; | ||||||
|  | 
 | ||||||
|  |         u8 alpha = 255; | ||||||
|  |         if (has_alpha) { | ||||||
|  |             u64_le packed_alpha; | ||||||
|  |             memcpy(&packed_alpha, subtile_ptr, sizeof(u64)); | ||||||
|  |             subtile_ptr += sizeof(u64); | ||||||
|  | 
 | ||||||
|  |             alpha = Color::Convert4To8((packed_alpha >> (4 * (x * subtile_width + y))) & 0xF); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         u64_le subtile_data; | ||||||
|  |         memcpy(&subtile_data, subtile_ptr, sizeof(u64)); | ||||||
|  | 
 | ||||||
|  |         return Math::MakeVec(SampleETC1Subtile(subtile_data, x, y), | ||||||
|  |                              disable_alpha ? (u8)255 : alpha); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format); | ||||||
|  |         DEBUG_ASSERT(false); | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, | ||||||
|  |                                           const Regs::TextureFormat& format) { | ||||||
|  |     TextureInfo info; | ||||||
|  |     info.physical_address = config.GetPhysicalAddress(); | ||||||
|  |     info.width = config.width; | ||||||
|  |     info.height = config.height; | ||||||
|  |     info.format = format; | ||||||
|  |     info.SetDefaultStride(); | ||||||
|  |     return info; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Texture
 | ||||||
|  | } // namespace Pica
 | ||||||
							
								
								
									
										60
									
								
								src/video_core/texture/texture_decode.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/video_core/texture/texture_decode.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/vector_math.h" | ||||||
|  | #include "video_core/pica.h" | ||||||
|  | 
 | ||||||
|  | namespace Pica { | ||||||
|  | namespace Texture { | ||||||
|  | 
 | ||||||
|  | /// Returns the byte size of a 8*8 tile of the specified texture format.
 | ||||||
|  | size_t CalculateTileSize(Pica::Regs::TextureFormat format); | ||||||
|  | 
 | ||||||
|  | struct TextureInfo { | ||||||
|  |     PAddr physical_address; | ||||||
|  |     unsigned int width; | ||||||
|  |     unsigned int height; | ||||||
|  |     ptrdiff_t stride; | ||||||
|  |     Pica::Regs::TextureFormat format; | ||||||
|  | 
 | ||||||
|  |     static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config, | ||||||
|  |                                         const Pica::Regs::TextureFormat& format); | ||||||
|  | 
 | ||||||
|  |     /// Calculates stride from format and width, assuming that the entire texture is contiguous.
 | ||||||
|  |     void SetDefaultStride() { | ||||||
|  |         stride = Pica::Texture::CalculateTileSize(format) * (width / 8); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Lookup texel located at the given coordinates and return an RGBA vector of its color. | ||||||
|  |  * @param source Source pointer to read data from | ||||||
|  |  * @param x,y Texture coordinates to read from | ||||||
|  |  * @param info TextureInfo object describing the texture setup | ||||||
|  |  * @param disable_alpha This is used for debug widgets which use this method to display textures | ||||||
|  |  * without providing a good way to visualize alpha by themselves. If true, this will return 255 for | ||||||
|  |  * the alpha component, and either drop the information entirely or store it in an "unused" color | ||||||
|  |  * channel. | ||||||
|  |  * @todo Eventually we should get rid of the disable_alpha parameter. | ||||||
|  |  */ | ||||||
|  | Math::Vec4<u8> LookupTexture(const u8* source, unsigned int x, unsigned int y, | ||||||
|  |                              const TextureInfo& info, bool disable_alpha = false); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Looks up a texel from a single 8x8 texture tile. | ||||||
|  |  * | ||||||
|  |  * @param source Pointer to the beginning of the tile. | ||||||
|  |  * @param x, y In-tile coordinates to read from. Must be < 8. | ||||||
|  |  * @param info TextureInfo describing the texture format. | ||||||
|  |  * @param disable_alpha Used for debugging. Sets the result alpha to 255 and either discards the | ||||||
|  |  *                      real alpha or inserts it in an otherwise unused channel. | ||||||
|  |  */ | ||||||
|  | Math::Vec4<u8> LookupTexelInTile(const u8* source, unsigned int x, unsigned int y, | ||||||
|  |                                  const TextureInfo& info, bool disable_alpha); | ||||||
|  | 
 | ||||||
|  | } // namespace Texture
 | ||||||
|  | } // namespace Pica
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue