diff options
| author | 2014-12-06 19:10:08 +0100 | |
|---|---|---|
| committer | 2014-12-20 18:05:53 +0100 | |
| commit | fd2539121cddd6177a964770a6985f8880ca1646 (patch) | |
| tree | 67d2e0e33ee435569e3a82eaa891c2154ed83d53 | |
| parent | BitField: Add an explicit Assign method. (diff) | |
| download | yuzu-fd2539121cddd6177a964770a6985f8880ca1646.tar.gz yuzu-fd2539121cddd6177a964770a6985f8880ca1646.tar.xz yuzu-fd2539121cddd6177a964770a6985f8880ca1646.zip | |
Pica: Initial support for multitexturing.
| -rw-r--r-- | src/citra_qt/debugger/graphics_cmdlists.cpp | 39 | ||||
| -rw-r--r-- | src/video_core/pica.h | 40 | ||||
| -rw-r--r-- | src/video_core/rasterizer.cpp | 58 | ||||
| -rw-r--r-- | src/video_core/vertex_shader.h | 9 |
4 files changed, 115 insertions, 31 deletions
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index 7f97cf143..bdd676470 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp | |||
| @@ -223,9 +223,21 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& | |||
| 223 | 223 | ||
| 224 | void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { | 224 | void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { |
| 225 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | 225 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); |
| 226 | if (COMMAND_IN_RANGE(command_id, texture0)) { | 226 | if (COMMAND_IN_RANGE(command_id, texture0) || |
| 227 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0, | 227 | COMMAND_IN_RANGE(command_id, texture1) || |
| 228 | Pica::registers.texture0_format); | 228 | COMMAND_IN_RANGE(command_id, texture2)) { |
| 229 | |||
| 230 | unsigned index; | ||
| 231 | if (COMMAND_IN_RANGE(command_id, texture0)) { | ||
| 232 | index = 0; | ||
| 233 | } else if (COMMAND_IN_RANGE(command_id, texture1)) { | ||
| 234 | index = 1; | ||
| 235 | } else { | ||
| 236 | index = 2; | ||
| 237 | } | ||
| 238 | auto config = Pica::registers.GetTextures()[index].config; | ||
| 239 | auto format = Pica::registers.GetTextures()[index].format; | ||
| 240 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); | ||
| 229 | 241 | ||
| 230 | // TODO: Instead, emit a signal here to be caught by the main window widget. | 242 | // TODO: Instead, emit a signal here to be caught by the main window widget. |
| 231 | auto main_window = static_cast<QMainWindow*>(parent()); | 243 | auto main_window = static_cast<QMainWindow*>(parent()); |
| @@ -237,10 +249,23 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { | |||
| 237 | QWidget* new_info_widget; | 249 | QWidget* new_info_widget; |
| 238 | 250 | ||
| 239 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | 251 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); |
| 240 | if (COMMAND_IN_RANGE(command_id, texture0)) { | 252 | if (COMMAND_IN_RANGE(command_id, texture0) || |
| 241 | u8* src = Memory::GetPointer(Pica::registers.texture0.GetPhysicalAddress()); | 253 | COMMAND_IN_RANGE(command_id, texture1) || |
| 242 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0, | 254 | COMMAND_IN_RANGE(command_id, texture2)) { |
| 243 | Pica::registers.texture0_format); | 255 | |
| 256 | unsigned index; | ||
| 257 | if (COMMAND_IN_RANGE(command_id, texture0)) { | ||
| 258 | index = 0; | ||
| 259 | } else if (COMMAND_IN_RANGE(command_id, texture1)) { | ||
| 260 | index = 1; | ||
| 261 | } else { | ||
| 262 | index = 2; | ||
| 263 | } | ||
| 264 | auto config = Pica::registers.GetTextures()[index].config; | ||
| 265 | auto format = Pica::registers.GetTextures()[index].format; | ||
| 266 | |||
| 267 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); | ||
| 268 | u8* src = Memory::GetPointer(config.GetPhysicalAddress()); | ||
| 244 | new_info_widget = new TextureInfoWidget(src, info); | 269 | new_info_widget = new TextureInfoWidget(src, info); |
| 245 | } else { | 270 | } else { |
| 246 | new_info_widget = new QWidget; | 271 | new_info_widget = new QWidget; |
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 4c3791ad9..92a87c086 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -155,12 +155,34 @@ struct Regs { | |||
| 155 | } | 155 | } |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | BitField< 0, 1, u32> texturing_enable; | 158 | union { |
| 159 | BitField< 0, 1, u32> texture0_enable; | ||
| 160 | BitField< 1, 1, u32> texture1_enable; | ||
| 161 | BitField< 2, 1, u32> texture2_enable; | ||
| 162 | }; | ||
| 159 | TextureConfig texture0; | 163 | TextureConfig texture0; |
| 160 | INSERT_PADDING_WORDS(0x8); | 164 | INSERT_PADDING_WORDS(0x8); |
| 161 | BitField<0, 4, TextureFormat> texture0_format; | 165 | BitField<0, 4, TextureFormat> texture0_format; |
| 162 | 166 | INSERT_PADDING_WORDS(0x2); | |
| 163 | INSERT_PADDING_WORDS(0x31); | 167 | TextureConfig texture1; |
| 168 | BitField<0, 4, TextureFormat> texture1_format; | ||
| 169 | INSERT_PADDING_WORDS(0x2); | ||
| 170 | TextureConfig texture2; | ||
| 171 | BitField<0, 4, TextureFormat> texture2_format; | ||
| 172 | INSERT_PADDING_WORDS(0x21); | ||
| 173 | |||
| 174 | struct FullTextureConfig { | ||
| 175 | const bool enabled; | ||
| 176 | const TextureConfig config; | ||
| 177 | const TextureFormat format; | ||
| 178 | }; | ||
| 179 | const std::array<FullTextureConfig, 3> GetTextures() const { | ||
| 180 | return {{ | ||
| 181 | { static_cast<bool>(texture0_enable), texture0, texture0_format }, | ||
| 182 | { static_cast<bool>(texture1_enable), texture1, texture1_format }, | ||
| 183 | { static_cast<bool>(texture2_enable), texture2, texture2_format } | ||
| 184 | }}; | ||
| 185 | } | ||
| 164 | 186 | ||
| 165 | // 0xc0-0xff: Texture Combiner (akin to glTexEnv) | 187 | // 0xc0-0xff: Texture Combiner (akin to glTexEnv) |
| 166 | struct TevStageConfig { | 188 | struct TevStageConfig { |
| @@ -556,9 +578,13 @@ struct Regs { | |||
| 556 | ADD_FIELD(viewport_depth_range); | 578 | ADD_FIELD(viewport_depth_range); |
| 557 | ADD_FIELD(viewport_depth_far_plane); | 579 | ADD_FIELD(viewport_depth_far_plane); |
| 558 | ADD_FIELD(viewport_corner); | 580 | ADD_FIELD(viewport_corner); |
| 559 | ADD_FIELD(texturing_enable); | 581 | ADD_FIELD(texture0_enable); |
| 560 | ADD_FIELD(texture0); | 582 | ADD_FIELD(texture0); |
| 561 | ADD_FIELD(texture0_format); | 583 | ADD_FIELD(texture0_format); |
| 584 | ADD_FIELD(texture1); | ||
| 585 | ADD_FIELD(texture1_format); | ||
| 586 | ADD_FIELD(texture2); | ||
| 587 | ADD_FIELD(texture2_format); | ||
| 562 | ADD_FIELD(tev_stage0); | 588 | ADD_FIELD(tev_stage0); |
| 563 | ADD_FIELD(tev_stage1); | 589 | ADD_FIELD(tev_stage1); |
| 564 | ADD_FIELD(tev_stage2); | 590 | ADD_FIELD(tev_stage2); |
| @@ -622,9 +648,13 @@ ASSERT_REG_POSITION(viewport_depth_far_plane, 0x4e); | |||
| 622 | ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); | 648 | ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); |
| 623 | ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); | 649 | ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); |
| 624 | ASSERT_REG_POSITION(viewport_corner, 0x68); | 650 | ASSERT_REG_POSITION(viewport_corner, 0x68); |
| 625 | ASSERT_REG_POSITION(texturing_enable, 0x80); | 651 | ASSERT_REG_POSITION(texture0_enable, 0x80); |
| 626 | ASSERT_REG_POSITION(texture0, 0x81); | 652 | ASSERT_REG_POSITION(texture0, 0x81); |
| 627 | ASSERT_REG_POSITION(texture0_format, 0x8e); | 653 | ASSERT_REG_POSITION(texture0_format, 0x8e); |
| 654 | ASSERT_REG_POSITION(texture1, 0x91); | ||
| 655 | ASSERT_REG_POSITION(texture1_format, 0x96); | ||
| 656 | ASSERT_REG_POSITION(texture2, 0x99); | ||
| 657 | ASSERT_REG_POSITION(texture2_format, 0x9e); | ||
| 628 | ASSERT_REG_POSITION(tev_stage0, 0xc0); | 658 | ASSERT_REG_POSITION(tev_stage0, 0xc0); |
| 629 | ASSERT_REG_POSITION(tev_stage1, 0xc8); | 659 | ASSERT_REG_POSITION(tev_stage1, 0xc8); |
| 630 | ASSERT_REG_POSITION(tev_stage2, 0xd0); | 660 | ASSERT_REG_POSITION(tev_stage2, 0xd0); |
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index b7e04a560..2ff6d19a6 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -167,10 +167,22 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 167 | (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255) | 167 | (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255) |
| 168 | }; | 168 | }; |
| 169 | 169 | ||
| 170 | Math::Vec4<u8> texture_color{}; | 170 | Math::Vec2<float24> uv[3]; |
| 171 | float24 u = GetInterpolatedAttribute(v0.tc0.u(), v1.tc0.u(), v2.tc0.u()); | 171 | uv[0].u() = GetInterpolatedAttribute(v0.tc0.u(), v1.tc0.u(), v2.tc0.u()); |
| 172 | float24 v = GetInterpolatedAttribute(v0.tc0.v(), v1.tc0.v(), v2.tc0.v()); | 172 | uv[0].v() = GetInterpolatedAttribute(v0.tc0.v(), v1.tc0.v(), v2.tc0.v()); |
| 173 | if (registers.texturing_enable) { | 173 | uv[1].u() = GetInterpolatedAttribute(v0.tc1.u(), v1.tc1.u(), v2.tc1.u()); |
| 174 | uv[1].v() = GetInterpolatedAttribute(v0.tc1.v(), v1.tc1.v(), v2.tc1.v()); | ||
| 175 | uv[2].u() = GetInterpolatedAttribute(v0.tc2.u(), v1.tc2.u(), v2.tc2.u()); | ||
| 176 | uv[2].v() = GetInterpolatedAttribute(v0.tc2.v(), v1.tc2.v(), v2.tc2.v()); | ||
| 177 | |||
| 178 | Math::Vec4<u8> texture_color[3]{}; | ||
| 179 | for (int i = 0; i < 3; ++i) { | ||
| 180 | auto texture = registers.GetTextures()[i]; | ||
| 181 | if (!texture.enabled) | ||
| 182 | continue; | ||
| 183 | |||
| 184 | _dbg_assert_(GPU, 0 != texture.config.address); | ||
| 185 | |||
| 174 | // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each | 186 | // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each |
| 175 | // of which is composed of four 2x2 subtiles each of which is composed of four texels. | 187 | // of which is composed of four 2x2 subtiles each of which is composed of four texels. |
| 176 | // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g. | 188 | // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g. |
| @@ -189,14 +201,11 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 189 | // 02 03 06 07 18 19 22 23 | 201 | // 02 03 06 07 18 19 22 23 |
| 190 | // 00 01 04 05 16 17 20 21 | 202 | // 00 01 04 05 16 17 20 21 |
| 191 | 203 | ||
| 192 | // TODO: This is currently hardcoded for RGB8 | ||
| 193 | u32* texture_data = (u32*)Memory::GetPointer(registers.texture0.GetPhysicalAddress()); | ||
| 194 | |||
| 195 | // TODO(neobrain): Not sure if this swizzling pattern is used for all textures. | 204 | // TODO(neobrain): Not sure if this swizzling pattern is used for all textures. |
| 196 | // To be flexible in case different but similar patterns are used, we keep this | 205 | // To be flexible in case different but similar patterns are used, we keep this |
| 197 | // somewhat inefficient code around for now. | 206 | // somewhat inefficient code around for now. |
| 198 | int s = (int)(u * float24::FromFloat32(static_cast<float>(registers.texture0.width))).ToFloat32(); | 207 | int s = (int)(uv[i].u() * float24::FromFloat32(static_cast<float>(texture.config.width))).ToFloat32(); |
| 199 | int t = (int)(v * float24::FromFloat32(static_cast<float>(registers.texture0.height))).ToFloat32(); | 208 | int t = (int)(uv[i].v() * float24::FromFloat32(static_cast<float>(texture.config.height))).ToFloat32(); |
| 200 | int texel_index_within_tile = 0; | 209 | int texel_index_within_tile = 0; |
| 201 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { | 210 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { |
| 202 | int sub_tile_width = 1 << block_size_index; | 211 | int sub_tile_width = 1 << block_size_index; |
| @@ -213,14 +222,17 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 213 | int coarse_s = (s / block_width) * block_width; | 222 | int coarse_s = (s / block_width) * block_width; |
| 214 | int coarse_t = (t / block_height) * block_height; | 223 | int coarse_t = (t / block_height) * block_height; |
| 215 | 224 | ||
| 216 | const int row_stride = registers.texture0.width * 3; | 225 | // TODO: This is currently hardcoded for RGB8 |
| 226 | u32* texture_data = (u32*)Memory::GetPointer(texture.config.GetPhysicalAddress()); | ||
| 227 | |||
| 228 | const int row_stride = texture.config.width * 3; | ||
| 217 | u8* source_ptr = (u8*)texture_data + coarse_s * block_height * 3 + coarse_t * row_stride + texel_index_within_tile * 3; | 229 | u8* source_ptr = (u8*)texture_data + coarse_s * block_height * 3 + coarse_t * row_stride + texel_index_within_tile * 3; |
| 218 | texture_color.r() = source_ptr[2]; | 230 | texture_color[i].r() = source_ptr[2]; |
| 219 | texture_color.g() = source_ptr[1]; | 231 | texture_color[i].g() = source_ptr[1]; |
| 220 | texture_color.b() = source_ptr[0]; | 232 | texture_color[i].b() = source_ptr[0]; |
| 221 | texture_color.a() = 0xFF; | 233 | texture_color[i].a() = 0xFF; |
| 222 | 234 | ||
| 223 | DebugUtils::DumpTexture(registers.texture0, (u8*)texture_data); | 235 | DebugUtils::DumpTexture(texture.config, (u8*)texture_data); |
| 224 | } | 236 | } |
| 225 | 237 | ||
| 226 | // Texture environment - consists of 6 stages of color and alpha combining. | 238 | // Texture environment - consists of 6 stages of color and alpha combining. |
| @@ -243,7 +255,13 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 243 | return primary_color.rgb(); | 255 | return primary_color.rgb(); |
| 244 | 256 | ||
| 245 | case Source::Texture0: | 257 | case Source::Texture0: |
| 246 | return texture_color.rgb(); | 258 | return texture_color[0].rgb(); |
| 259 | |||
| 260 | case Source::Texture1: | ||
| 261 | return texture_color[1].rgb(); | ||
| 262 | |||
| 263 | case Source::Texture2: | ||
| 264 | return texture_color[2].rgb(); | ||
| 247 | 265 | ||
| 248 | case Source::Constant: | 266 | case Source::Constant: |
| 249 | return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b}; | 267 | return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b}; |
| @@ -263,7 +281,13 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 263 | return primary_color.a(); | 281 | return primary_color.a(); |
| 264 | 282 | ||
| 265 | case Source::Texture0: | 283 | case Source::Texture0: |
| 266 | return texture_color.a(); | 284 | return texture_color[0].a(); |
| 285 | |||
| 286 | case Source::Texture1: | ||
| 287 | return texture_color[1].a(); | ||
| 288 | |||
| 289 | case Source::Texture2: | ||
| 290 | return texture_color[2].a(); | ||
| 267 | 291 | ||
| 268 | case Source::Constant: | 292 | case Source::Constant: |
| 269 | return tev_stage.const_a; | 293 | return tev_stage.const_a; |
diff --git a/src/video_core/vertex_shader.h b/src/video_core/vertex_shader.h index bfb6fb6e3..c1292fc2d 100644 --- a/src/video_core/vertex_shader.h +++ b/src/video_core/vertex_shader.h | |||
| @@ -27,15 +27,18 @@ struct OutputVertex { | |||
| 27 | Math::Vec4<float24> dummy; // quaternions (not implemented, yet) | 27 | Math::Vec4<float24> dummy; // quaternions (not implemented, yet) |
| 28 | Math::Vec4<float24> color; | 28 | Math::Vec4<float24> color; |
| 29 | Math::Vec2<float24> tc0; | 29 | Math::Vec2<float24> tc0; |
| 30 | Math::Vec2<float24> tc1; | ||
| 31 | float24 pad[6]; | ||
| 32 | Math::Vec2<float24> tc2; | ||
| 30 | 33 | ||
| 31 | // Padding for optimal alignment | 34 | // Padding for optimal alignment |
| 32 | float24 pad[14]; | 35 | float24 pad2[4]; |
| 33 | 36 | ||
| 34 | // Attributes used to store intermediate results | 37 | // Attributes used to store intermediate results |
| 35 | 38 | ||
| 36 | // position after perspective divide | 39 | // position after perspective divide |
| 37 | Math::Vec3<float24> screenpos; | 40 | Math::Vec3<float24> screenpos; |
| 38 | float24 pad2; | 41 | float24 pad3; |
| 39 | 42 | ||
| 40 | // Linear interpolation | 43 | // Linear interpolation |
| 41 | // factor: 0=this, 1=vtx | 44 | // factor: 0=this, 1=vtx |
| @@ -44,6 +47,8 @@ struct OutputVertex { | |||
| 44 | 47 | ||
| 45 | // TODO: Should perform perspective correct interpolation here... | 48 | // TODO: Should perform perspective correct interpolation here... |
| 46 | tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor); | 49 | tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor); |
| 50 | tc1 = tc1 * factor + vtx.tc1 * (float24::FromFloat32(1) - factor); | ||
| 51 | tc2 = tc2 * factor + vtx.tc2 * (float24::FromFloat32(1) - factor); | ||
| 47 | 52 | ||
| 48 | screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor); | 53 | screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor); |
| 49 | 54 | ||