summaryrefslogtreecommitdiff
path: root/src/video_core/debug_utils
diff options
context:
space:
mode:
authorGravatar Yuri Kunde Schlesner2016-09-21 11:29:48 -0700
committerGravatar GitHub2016-09-21 11:29:48 -0700
commitd5d2ca8058a0f1c00ab7ca9fe2c058ba47546c0a (patch)
tree8a22ca73ff838f3f0090b29a548ae81087fc90ed /src/video_core/debug_utils
parentREADME: Specify master branch for Travis CI badge (diff)
parentFix Travis clang-format check (diff)
downloadyuzu-d5d2ca8058a0f1c00ab7ca9fe2c058ba47546c0a.tar.gz
yuzu-d5d2ca8058a0f1c00ab7ca9fe2c058ba47546c0a.tar.xz
yuzu-d5d2ca8058a0f1c00ab7ca9fe2c058ba47546c0a.zip
Merge pull request #2086 from linkmauve/clang-format
Add clang-format as part of our {commit,travis}-time checks
Diffstat (limited to 'src/video_core/debug_utils')
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp429
-rw-r--r--src/video_core/debug_utils/debug_utils.h39
2 files changed, 239 insertions, 229 deletions
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index bfa686380..8806464d9 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -20,7 +20,6 @@
20#include <nihstro/bit_field.h> 20#include <nihstro/bit_field.h>
21#include <nihstro/float24.h> 21#include <nihstro/float24.h>
22#include <nihstro/shader_binary.h> 22#include <nihstro/shader_binary.h>
23
24#include "common/assert.h" 23#include "common/assert.h"
25#include "common/bit_field.h" 24#include "common/bit_field.h"
26#include "common/color.h" 25#include "common/color.h"
@@ -29,7 +28,6 @@
29#include "common/logging/log.h" 28#include "common/logging/log.h"
30#include "common/math_util.h" 29#include "common/math_util.h"
31#include "common/vector_math.h" 30#include "common/vector_math.h"
32
33#include "video_core/debug_utils/debug_utils.h" 31#include "video_core/debug_utils/debug_utils.h"
34#include "video_core/pica.h" 32#include "video_core/pica.h"
35#include "video_core/pica_state.h" 33#include "video_core/pica_state.h"
@@ -50,7 +48,8 @@ void DebugContext::DoOnEvent(Event event, void* data) {
50 { 48 {
51 std::unique_lock<std::mutex> lock(breakpoint_mutex); 49 std::unique_lock<std::mutex> lock(breakpoint_mutex);
52 50
53 // Commit the rasterizer's caches so framebuffers, render targets, etc. will show on debug widgets 51 // Commit the rasterizer's caches so framebuffers, render targets, etc. will show on debug
52 // widgets
54 VideoCore::g_renderer->Rasterizer()->FlushAll(); 53 VideoCore::g_renderer->Rasterizer()->FlushAll();
55 54
56 // TODO: Should stop the CPU thread here once we multithread emulation. 55 // TODO: Should stop the CPU thread here once we multithread emulation.
@@ -64,7 +63,7 @@ void DebugContext::DoOnEvent(Event event, void* data) {
64 } 63 }
65 64
66 // Wait until another thread tells us to Resume() 65 // Wait until another thread tells us to Resume()
67 resume_from_breakpoint.wait(lock, [&]{ return !at_breakpoint; }); 66 resume_from_breakpoint.wait(lock, [&] { return !at_breakpoint; });
68 } 67 }
69} 68}
70 69
@@ -88,8 +87,9 @@ std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
88 87
89namespace DebugUtils { 88namespace DebugUtils {
90 89
91void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes) 90void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
92{ 91 const Shader::ShaderSetup& setup,
92 const Regs::VSOutputAttributes* output_attributes) {
93 struct StuffToWrite { 93 struct StuffToWrite {
94 const u8* pointer; 94 const u8* pointer;
95 u32 size; 95 u32 size;
@@ -97,7 +97,7 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
97 std::vector<StuffToWrite> writing_queue; 97 std::vector<StuffToWrite> writing_queue;
98 u32 write_offset = 0; 98 u32 write_offset = 0;
99 99
100 auto QueueForWriting = [&writing_queue,&write_offset](const u8* pointer, u32 size) { 100 auto QueueForWriting = [&writing_queue, &write_offset](const u8* pointer, u32 size) {
101 writing_queue.push_back({pointer, size}); 101 writing_queue.push_back({pointer, size});
102 u32 old_write_offset = write_offset; 102 u32 old_write_offset = write_offset;
103 write_offset += size; 103 write_offset += size;
@@ -108,99 +108,95 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
108 // into shbin format (separate type and component mask). 108 // into shbin format (separate type and component mask).
109 union OutputRegisterInfo { 109 union OutputRegisterInfo {
110 enum Type : u64 { 110 enum Type : u64 {
111 POSITION = 0, 111 POSITION = 0,
112 QUATERNION = 1, 112 QUATERNION = 1,
113 COLOR = 2, 113 COLOR = 2,
114 TEXCOORD0 = 3, 114 TEXCOORD0 = 3,
115 TEXCOORD1 = 5, 115 TEXCOORD1 = 5,
116 TEXCOORD2 = 6, 116 TEXCOORD2 = 6,
117 117
118 VIEW = 8, 118 VIEW = 8,
119 }; 119 };
120 120
121 BitField< 0, 64, u64> hex; 121 BitField<0, 64, u64> hex;
122 122
123 BitField< 0, 16, Type> type; 123 BitField<0, 16, Type> type;
124 BitField<16, 16, u64> id; 124 BitField<16, 16, u64> id;
125 BitField<32, 4, u64> component_mask; 125 BitField<32, 4, u64> component_mask;
126 }; 126 };
127 127
128 // This is put into a try-catch block to make sure we notice unknown configurations. 128 // This is put into a try-catch block to make sure we notice unknown configurations.
129 std::vector<OutputRegisterInfo> output_info_table; 129 std::vector<OutputRegisterInfo> output_info_table;
130 for (unsigned i = 0; i < 7; ++i) { 130 for (unsigned i = 0; i < 7; ++i) {
131 using OutputAttributes = Pica::Regs::VSOutputAttributes; 131 using OutputAttributes = Pica::Regs::VSOutputAttributes;
132 132
133 // TODO: It's still unclear how the attribute components map to the register! 133 // TODO: It's still unclear how the attribute components map to the register!
134 // Once we know that, this code probably will not make much sense anymore. 134 // Once we know that, this code probably will not make much sense anymore.
135 std::map<OutputAttributes::Semantic, std::pair<OutputRegisterInfo::Type, u32> > map = { 135 std::map<OutputAttributes::Semantic, std::pair<OutputRegisterInfo::Type, u32>> map = {
136 { OutputAttributes::POSITION_X, { OutputRegisterInfo::POSITION, 1} }, 136 {OutputAttributes::POSITION_X, {OutputRegisterInfo::POSITION, 1}},
137 { OutputAttributes::POSITION_Y, { OutputRegisterInfo::POSITION, 2} }, 137 {OutputAttributes::POSITION_Y, {OutputRegisterInfo::POSITION, 2}},
138 { OutputAttributes::POSITION_Z, { OutputRegisterInfo::POSITION, 4} }, 138 {OutputAttributes::POSITION_Z, {OutputRegisterInfo::POSITION, 4}},
139 { OutputAttributes::POSITION_W, { OutputRegisterInfo::POSITION, 8} }, 139 {OutputAttributes::POSITION_W, {OutputRegisterInfo::POSITION, 8}},
140 { OutputAttributes::QUATERNION_X, { OutputRegisterInfo::QUATERNION, 1} }, 140 {OutputAttributes::QUATERNION_X, {OutputRegisterInfo::QUATERNION, 1}},
141 { OutputAttributes::QUATERNION_Y, { OutputRegisterInfo::QUATERNION, 2} }, 141 {OutputAttributes::QUATERNION_Y, {OutputRegisterInfo::QUATERNION, 2}},
142 { OutputAttributes::QUATERNION_Z, { OutputRegisterInfo::QUATERNION, 4} }, 142 {OutputAttributes::QUATERNION_Z, {OutputRegisterInfo::QUATERNION, 4}},
143 { OutputAttributes::QUATERNION_W, { OutputRegisterInfo::QUATERNION, 8} }, 143 {OutputAttributes::QUATERNION_W, {OutputRegisterInfo::QUATERNION, 8}},
144 { OutputAttributes::COLOR_R, { OutputRegisterInfo::COLOR, 1} }, 144 {OutputAttributes::COLOR_R, {OutputRegisterInfo::COLOR, 1}},
145 { OutputAttributes::COLOR_G, { OutputRegisterInfo::COLOR, 2} }, 145 {OutputAttributes::COLOR_G, {OutputRegisterInfo::COLOR, 2}},
146 { OutputAttributes::COLOR_B, { OutputRegisterInfo::COLOR, 4} }, 146 {OutputAttributes::COLOR_B, {OutputRegisterInfo::COLOR, 4}},
147 { OutputAttributes::COLOR_A, { OutputRegisterInfo::COLOR, 8} }, 147 {OutputAttributes::COLOR_A, {OutputRegisterInfo::COLOR, 8}},
148 { OutputAttributes::TEXCOORD0_U, { OutputRegisterInfo::TEXCOORD0, 1} }, 148 {OutputAttributes::TEXCOORD0_U, {OutputRegisterInfo::TEXCOORD0, 1}},
149 { OutputAttributes::TEXCOORD0_V, { OutputRegisterInfo::TEXCOORD0, 2} }, 149 {OutputAttributes::TEXCOORD0_V, {OutputRegisterInfo::TEXCOORD0, 2}},
150 { OutputAttributes::TEXCOORD1_U, { OutputRegisterInfo::TEXCOORD1, 1} }, 150 {OutputAttributes::TEXCOORD1_U, {OutputRegisterInfo::TEXCOORD1, 1}},
151 { OutputAttributes::TEXCOORD1_V, { OutputRegisterInfo::TEXCOORD1, 2} }, 151 {OutputAttributes::TEXCOORD1_V, {OutputRegisterInfo::TEXCOORD1, 2}},
152 { OutputAttributes::TEXCOORD2_U, { OutputRegisterInfo::TEXCOORD2, 1} }, 152 {OutputAttributes::TEXCOORD2_U, {OutputRegisterInfo::TEXCOORD2, 1}},
153 { OutputAttributes::TEXCOORD2_V, { OutputRegisterInfo::TEXCOORD2, 2} }, 153 {OutputAttributes::TEXCOORD2_V, {OutputRegisterInfo::TEXCOORD2, 2}},
154 { OutputAttributes::VIEW_X, { OutputRegisterInfo::VIEW, 1} }, 154 {OutputAttributes::VIEW_X, {OutputRegisterInfo::VIEW, 1}},
155 { OutputAttributes::VIEW_Y, { OutputRegisterInfo::VIEW, 2} }, 155 {OutputAttributes::VIEW_Y, {OutputRegisterInfo::VIEW, 2}},
156 { OutputAttributes::VIEW_Z, { OutputRegisterInfo::VIEW, 4} } 156 {OutputAttributes::VIEW_Z, {OutputRegisterInfo::VIEW, 4}},
157 }; 157 };
158 158
159 for (const auto& semantic : std::vector<OutputAttributes::Semantic>{ 159 for (const auto& semantic : std::vector<OutputAttributes::Semantic>{
160 output_attributes[i].map_x, 160 output_attributes[i].map_x, output_attributes[i].map_y, output_attributes[i].map_z,
161 output_attributes[i].map_y, 161 output_attributes[i].map_w}) {
162 output_attributes[i].map_z, 162 if (semantic == OutputAttributes::INVALID)
163 output_attributes[i].map_w }) { 163 continue;
164 if (semantic == OutputAttributes::INVALID) 164
165 continue; 165 try {
166 166 OutputRegisterInfo::Type type = map.at(semantic).first;
167 try { 167 u32 component_mask = map.at(semantic).second;
168 OutputRegisterInfo::Type type = map.at(semantic).first; 168
169 u32 component_mask = map.at(semantic).second; 169 auto it = std::find_if(output_info_table.begin(), output_info_table.end(),
170 170 [&i, &type](const OutputRegisterInfo& info) {
171 auto it = std::find_if(output_info_table.begin(), output_info_table.end(), 171 return info.id == i && info.type == type;
172 [&i, &type](const OutputRegisterInfo& info) { 172 });
173 return info.id == i && info.type == type; 173
174 } 174 if (it == output_info_table.end()) {
175 ); 175 output_info_table.emplace_back();
176 176 output_info_table.back().type.Assign(type);
177 if (it == output_info_table.end()) { 177 output_info_table.back().component_mask.Assign(component_mask);
178 output_info_table.emplace_back(); 178 output_info_table.back().id.Assign(i);
179 output_info_table.back().type.Assign(type); 179 } else {
180 output_info_table.back().component_mask.Assign(component_mask); 180 it->component_mask.Assign(it->component_mask | component_mask);
181 output_info_table.back().id.Assign(i);
182 } else {
183 it->component_mask.Assign(it->component_mask | component_mask);
184 }
185 } catch (const std::out_of_range& ) {
186 DEBUG_ASSERT_MSG(false, "Unknown output attribute mapping");
187 LOG_ERROR(HW_GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x",
188 (int)output_attributes[i].map_x.Value(),
189 (int)output_attributes[i].map_y.Value(),
190 (int)output_attributes[i].map_z.Value(),
191 (int)output_attributes[i].map_w.Value());
192 } 181 }
182 } catch (const std::out_of_range&) {
183 DEBUG_ASSERT_MSG(false, "Unknown output attribute mapping");
184 LOG_ERROR(HW_GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x",
185 (int)output_attributes[i].map_x.Value(),
186 (int)output_attributes[i].map_y.Value(),
187 (int)output_attributes[i].map_z.Value(),
188 (int)output_attributes[i].map_w.Value());
193 } 189 }
194 } 190 }
195 191 }
196 192
197 struct { 193 struct {
198 DVLBHeader header; 194 DVLBHeader header;
199 u32 dvle_offset; 195 u32 dvle_offset;
200 } dvlb{ {DVLBHeader::MAGIC_WORD, 1 } }; // 1 DVLE 196 } dvlb{{DVLBHeader::MAGIC_WORD, 1}}; // 1 DVLE
201 197
202 DVLPHeader dvlp{ DVLPHeader::MAGIC_WORD }; 198 DVLPHeader dvlp{DVLPHeader::MAGIC_WORD};
203 DVLEHeader dvle{ DVLEHeader::MAGIC_WORD }; 199 DVLEHeader dvle{DVLEHeader::MAGIC_WORD};
204 200
205 QueueForWriting(reinterpret_cast<const u8*>(&dvlb), sizeof(dvlb)); 201 QueueForWriting(reinterpret_cast<const u8*>(&dvlb), sizeof(dvlb));
206 u32 dvlp_offset = QueueForWriting(reinterpret_cast<const u8*>(&dvlp), sizeof(dvlp)); 202 u32 dvlp_offset = QueueForWriting(reinterpret_cast<const u8*>(&dvlp), sizeof(dvlp));
@@ -216,14 +212,16 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
216 dvlp.swizzle_info_num_entries = static_cast<uint32_t>(setup.swizzle_data.size()); 212 dvlp.swizzle_info_num_entries = static_cast<uint32_t>(setup.swizzle_data.size());
217 u32 dummy = 0; 213 u32 dummy = 0;
218 for (unsigned int i = 0; i < setup.swizzle_data.size(); ++i) { 214 for (unsigned int i = 0; i < setup.swizzle_data.size(); ++i) {
219 QueueForWriting(reinterpret_cast<const u8*>(&setup.swizzle_data[i]), sizeof(setup.swizzle_data[i])); 215 QueueForWriting(reinterpret_cast<const u8*>(&setup.swizzle_data[i]),
216 sizeof(setup.swizzle_data[i]));
220 QueueForWriting(reinterpret_cast<const u8*>(&dummy), sizeof(dummy)); 217 QueueForWriting(reinterpret_cast<const u8*>(&dummy), sizeof(dummy));
221 } 218 }
222 219
223 dvle.main_offset_words = config.main_offset; 220 dvle.main_offset_words = config.main_offset;
224 dvle.output_register_table_offset = write_offset - dvlb.dvle_offset; 221 dvle.output_register_table_offset = write_offset - dvlb.dvle_offset;
225 dvle.output_register_table_size = static_cast<u32>(output_info_table.size()); 222 dvle.output_register_table_size = static_cast<u32>(output_info_table.size());
226 QueueForWriting(reinterpret_cast<const u8*>(output_info_table.data()), static_cast<u32>(output_info_table.size() * sizeof(OutputRegisterInfo))); 223 QueueForWriting(reinterpret_cast<const u8*>(output_info_table.data()),
224 static_cast<u32>(output_info_table.size() * sizeof(OutputRegisterInfo)));
227 225
228 // TODO: Create a label table for "main" 226 // TODO: Create a label table for "main"
229 227
@@ -258,10 +256,8 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
258 constant.f.w = nihstro::to_float24(setup.uniforms.f[i].w.ToFloat32()); 256 constant.f.w = nihstro::to_float24(setup.uniforms.f[i].w.ToFloat32());
259 257
260 // Store constant if it's different from zero.. 258 // Store constant if it's different from zero..
261 if (setup.uniforms.f[i].x.ToFloat32() != 0.0 || 259 if (setup.uniforms.f[i].x.ToFloat32() != 0.0 || setup.uniforms.f[i].y.ToFloat32() != 0.0 ||
262 setup.uniforms.f[i].y.ToFloat32() != 0.0 || 260 setup.uniforms.f[i].z.ToFloat32() != 0.0 || setup.uniforms.f[i].w.ToFloat32() != 0.0)
263 setup.uniforms.f[i].z.ToFloat32() != 0.0 ||
264 setup.uniforms.f[i].w.ToFloat32() != 0.0)
265 constant_table.emplace_back(constant); 261 constant_table.emplace_back(constant);
266 } 262 }
267 dvle.constant_table_offset = write_offset - dvlb.dvle_offset; 263 dvle.constant_table_offset = write_offset - dvlb.dvle_offset;
@@ -282,8 +278,7 @@ static std::unique_ptr<PicaTrace> pica_trace;
282static std::mutex pica_trace_mutex; 278static std::mutex pica_trace_mutex;
283static int is_pica_tracing = false; 279static int is_pica_tracing = false;
284 280
285void StartPicaTracing() 281void StartPicaTracing() {
286{
287 if (is_pica_tracing) { 282 if (is_pica_tracing) {
288 LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!"); 283 LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!");
289 return; 284 return;
@@ -295,13 +290,11 @@ void StartPicaTracing()
295 is_pica_tracing = true; 290 is_pica_tracing = true;
296} 291}
297 292
298bool IsPicaTracing() 293bool IsPicaTracing() {
299{
300 return is_pica_tracing != 0; 294 return is_pica_tracing != 0;
301} 295}
302 296
303void OnPicaRegWrite(PicaTrace::Write write) 297void OnPicaRegWrite(PicaTrace::Write write) {
304{
305 // Double check for is_pica_tracing to avoid pointless locking overhead 298 // Double check for is_pica_tracing to avoid pointless locking overhead
306 if (!is_pica_tracing) 299 if (!is_pica_tracing)
307 return; 300 return;
@@ -314,8 +307,7 @@ void OnPicaRegWrite(PicaTrace::Write write)
314 pica_trace->writes.push_back(write); 307 pica_trace->writes.push_back(write);
315} 308}
316 309
317std::unique_ptr<PicaTrace> FinishPicaTracing() 310std::unique_ptr<PicaTrace> FinishPicaTracing() {
318{
319 if (!is_pica_tracing) { 311 if (!is_pica_tracing) {
320 LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!"); 312 LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!");
321 return {}; 313 return {};
@@ -331,12 +323,12 @@ std::unique_ptr<PicaTrace> FinishPicaTracing()
331 return ret; 323 return ret;
332} 324}
333 325
334const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info, bool disable_alpha) { 326const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info,
327 bool disable_alpha) {
335 const unsigned int coarse_x = x & ~7; 328 const unsigned int coarse_x = x & ~7;
336 const unsigned int coarse_y = y & ~7; 329 const unsigned int coarse_y = y & ~7;
337 330
338 if (info.format != Regs::TextureFormat::ETC1 && 331 if (info.format != Regs::TextureFormat::ETC1 && info.format != Regs::TextureFormat::ETC1A4) {
339 info.format != Regs::TextureFormat::ETC1A4) {
340 // TODO(neobrain): Fix code design to unify vertical block offsets! 332 // TODO(neobrain): Fix code design to unify vertical block offsets!
341 source += coarse_y * info.stride; 333 source += coarse_y * info.stride;
342 } 334 }
@@ -344,73 +336,63 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
344 // TODO: Assert that width/height are multiples of block dimensions 336 // TODO: Assert that width/height are multiples of block dimensions
345 337
346 switch (info.format) { 338 switch (info.format) {
347 case Regs::TextureFormat::RGBA8: 339 case Regs::TextureFormat::RGBA8: {
348 {
349 auto res = Color::DecodeRGBA8(source + VideoCore::GetMortonOffset(x, y, 4)); 340 auto res = Color::DecodeRGBA8(source + VideoCore::GetMortonOffset(x, y, 4));
350 return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) }; 341 return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())};
351 } 342 }
352 343
353 case Regs::TextureFormat::RGB8: 344 case Regs::TextureFormat::RGB8: {
354 {
355 auto res = Color::DecodeRGB8(source + VideoCore::GetMortonOffset(x, y, 3)); 345 auto res = Color::DecodeRGB8(source + VideoCore::GetMortonOffset(x, y, 3));
356 return { res.r(), res.g(), res.b(), 255 }; 346 return {res.r(), res.g(), res.b(), 255};
357 } 347 }
358 348
359 case Regs::TextureFormat::RGB5A1: 349 case Regs::TextureFormat::RGB5A1: {
360 {
361 auto res = Color::DecodeRGB5A1(source + VideoCore::GetMortonOffset(x, y, 2)); 350 auto res = Color::DecodeRGB5A1(source + VideoCore::GetMortonOffset(x, y, 2));
362 return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) }; 351 return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())};
363 } 352 }
364 353
365 case Regs::TextureFormat::RGB565: 354 case Regs::TextureFormat::RGB565: {
366 {
367 auto res = Color::DecodeRGB565(source + VideoCore::GetMortonOffset(x, y, 2)); 355 auto res = Color::DecodeRGB565(source + VideoCore::GetMortonOffset(x, y, 2));
368 return { res.r(), res.g(), res.b(), 255 }; 356 return {res.r(), res.g(), res.b(), 255};
369 } 357 }
370 358
371 case Regs::TextureFormat::RGBA4: 359 case Regs::TextureFormat::RGBA4: {
372 {
373 auto res = Color::DecodeRGBA4(source + VideoCore::GetMortonOffset(x, y, 2)); 360 auto res = Color::DecodeRGBA4(source + VideoCore::GetMortonOffset(x, y, 2));
374 return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) }; 361 return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())};
375 } 362 }
376 363
377 case Regs::TextureFormat::IA8: 364 case Regs::TextureFormat::IA8: {
378 {
379 const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 2); 365 const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 2);
380 366
381 if (disable_alpha) { 367 if (disable_alpha) {
382 // Show intensity as red, alpha as green 368 // Show intensity as red, alpha as green
383 return { source_ptr[1], source_ptr[0], 0, 255 }; 369 return {source_ptr[1], source_ptr[0], 0, 255};
384 } else { 370 } else {
385 return { source_ptr[1], source_ptr[1], source_ptr[1], source_ptr[0] }; 371 return {source_ptr[1], source_ptr[1], source_ptr[1], source_ptr[0]};
386 } 372 }
387 } 373 }
388 374
389 case Regs::TextureFormat::RG8: 375 case Regs::TextureFormat::RG8: {
390 {
391 auto res = Color::DecodeRG8(source + VideoCore::GetMortonOffset(x, y, 2)); 376 auto res = Color::DecodeRG8(source + VideoCore::GetMortonOffset(x, y, 2));
392 return { res.r(), res.g(), 0, 255 }; 377 return {res.r(), res.g(), 0, 255};
393 } 378 }
394 379
395 case Regs::TextureFormat::I8: 380 case Regs::TextureFormat::I8: {
396 {
397 const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1); 381 const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
398 return { *source_ptr, *source_ptr, *source_ptr, 255 }; 382 return {*source_ptr, *source_ptr, *source_ptr, 255};
399 } 383 }
400 384
401 case Regs::TextureFormat::A8: 385 case Regs::TextureFormat::A8: {
402 {
403 const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1); 386 const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
404 387
405 if (disable_alpha) { 388 if (disable_alpha) {
406 return { *source_ptr, *source_ptr, *source_ptr, 255 }; 389 return {*source_ptr, *source_ptr, *source_ptr, 255};
407 } else { 390 } else {
408 return { 0, 0, 0, *source_ptr }; 391 return {0, 0, 0, *source_ptr};
409 } 392 }
410 } 393 }
411 394
412 case Regs::TextureFormat::IA4: 395 case Regs::TextureFormat::IA4: {
413 {
414 const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1); 396 const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
415 397
416 u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4); 398 u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4);
@@ -418,25 +400,23 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
418 400
419 if (disable_alpha) { 401 if (disable_alpha) {
420 // Show intensity as red, alpha as green 402 // Show intensity as red, alpha as green
421 return { i, a, 0, 255 }; 403 return {i, a, 0, 255};
422 } else { 404 } else {
423 return { i, i, i, a }; 405 return {i, i, i, a};
424 } 406 }
425 } 407 }
426 408
427 case Regs::TextureFormat::I4: 409 case Regs::TextureFormat::I4: {
428 {
429 u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1); 410 u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1);
430 const u8* source_ptr = source + morton_offset / 2; 411 const u8* source_ptr = source + morton_offset / 2;
431 412
432 u8 i = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF); 413 u8 i = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF);
433 i = Color::Convert4To8(i); 414 i = Color::Convert4To8(i);
434 415
435 return { i, i, i, 255 }; 416 return {i, i, i, 255};
436 } 417 }
437 418
438 case Regs::TextureFormat::A4: 419 case Regs::TextureFormat::A4: {
439 {
440 u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1); 420 u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1);
441 const u8* source_ptr = source + morton_offset / 2; 421 const u8* source_ptr = source + morton_offset / 2;
442 422
@@ -444,15 +424,14 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
444 a = Color::Convert4To8(a); 424 a = Color::Convert4To8(a);
445 425
446 if (disable_alpha) { 426 if (disable_alpha) {
447 return { a, a, a, 255 }; 427 return {a, a, a, 255};
448 } else { 428 } else {
449 return { 0, 0, 0, a }; 429 return {0, 0, 0, a};
450 } 430 }
451 } 431 }
452 432
453 case Regs::TextureFormat::ETC1: 433 case Regs::TextureFormat::ETC1:
454 case Regs::TextureFormat::ETC1A4: 434 case Regs::TextureFormat::ETC1A4: {
455 {
456 bool has_alpha = (info.format == Regs::TextureFormat::ETC1A4); 435 bool has_alpha = (info.format == Regs::TextureFormat::ETC1A4);
457 436
458 // ETC1 further subdivides each 8x8 tile into four 4x4 subtiles 437 // ETC1 further subdivides each 8x8 tile into four 4x4 subtiles
@@ -462,10 +441,9 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
462 int subtile_index = ((x / subtile_width) & 1) + 2 * ((y / subtile_height) & 1); 441 int subtile_index = ((x / subtile_width) & 1) + 2 * ((y / subtile_height) & 1);
463 unsigned subtile_bytes = has_alpha ? 2 : 1; // TODO: Name... 442 unsigned subtile_bytes = has_alpha ? 2 : 1; // TODO: Name...
464 443
465 const u64* source_ptr = (const u64*)(source 444 const u64* source_ptr = (const u64*)(source + coarse_x * subtile_bytes * 4 +
466 + coarse_x * subtile_bytes * 4 445 coarse_y * subtile_bytes * 4 * (info.width / 8) +
467 + coarse_y * subtile_bytes * 4 * (info.width / 8) 446 subtile_index * subtile_bytes * 8);
468 + subtile_index * subtile_bytes * 8);
469 u64 alpha = 0xFFFFFFFFFFFFFFFF; 447 u64 alpha = 0xFFFFFFFFFFFFFFFF;
470 if (has_alpha) { 448 if (has_alpha) {
471 alpha = *source_ptr; 449 alpha = *source_ptr;
@@ -474,7 +452,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
474 452
475 union ETC1Tile { 453 union ETC1Tile {
476 // Each of these two is a collection of 16 bits (one per lookup value) 454 // Each of these two is a collection of 16 bits (one per lookup value)
477 BitField< 0, 16, u64> table_subindexes; 455 BitField<0, 16, u64> table_subindexes;
478 BitField<16, 16, u64> negation_flags; 456 BitField<16, 16, u64> negation_flags;
479 457
480 unsigned GetTableSubIndex(unsigned index) const { 458 unsigned GetTableSubIndex(unsigned index) const {
@@ -547,11 +525,18 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
547 } 525 }
548 526
549 // Add modifier 527 // Add modifier
550 unsigned table_index = static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value()); 528 unsigned table_index =
529 static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value());
551 530
552 static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{ 531 static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{
553 {{ 2, 8 }}, {{ 5, 17 }}, {{ 9, 29 }}, {{ 13, 42 }}, 532 {{2, 8}},
554 {{ 18, 60 }}, {{ 24, 80 }}, {{ 33, 106 }}, {{ 47, 183 }} 533 {{5, 17}},
534 {{9, 29}},
535 {{13, 42}},
536 {{18, 60}},
537 {{24, 80}},
538 {{33, 106}},
539 {{47, 183}},
555 }}; 540 }};
556 541
557 int modifier = etc1_modifier_table.at(table_index).at(GetTableSubIndex(texel)); 542 int modifier = etc1_modifier_table.at(table_index).at(GetTableSubIndex(texel));
@@ -564,7 +549,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
564 549
565 return ret.Cast<u8>(); 550 return ret.Cast<u8>();
566 } 551 }
567 } const *etc1_tile = reinterpret_cast<const ETC1Tile*>(source_ptr); 552 } const* etc1_tile = reinterpret_cast<const ETC1Tile*>(source_ptr);
568 553
569 alpha >>= 4 * ((x & 3) * 4 + (y & 3)); 554 alpha >>= 4 * ((x & 3) * 4 + (y & 3));
570 return Math::MakeVec(etc1_tile->GetRGB(x & 3, y & 3), 555 return Math::MakeVec(etc1_tile->GetRGB(x & 3, y & 3),
@@ -579,8 +564,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
579} 564}
580 565
581TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, 566TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config,
582 const Regs::TextureFormat& format) 567 const Regs::TextureFormat& format) {
583{
584 TextureInfo info; 568 TextureInfo info;
585 info.physical_address = config.GetPhysicalAddress(); 569 info.physical_address = config.GetPhysicalAddress();
586 info.width = config.width; 570 info.width = config.width;
@@ -595,13 +579,13 @@ TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config,
595static void WriteIOFile(png_structp png_ptr, png_bytep data, png_size_t length) { 579static void WriteIOFile(png_structp png_ptr, png_bytep data, png_size_t length) {
596 auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr)); 580 auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr));
597 if (!fp->WriteBytes(data, length)) 581 if (!fp->WriteBytes(data, length))
598 png_error(png_ptr, "Failed to write to output PNG file."); 582 png_error(png_ptr, "Failed to write to output PNG file.");
599} 583}
600 584
601static void FlushIOFile(png_structp png_ptr) { 585static void FlushIOFile(png_structp png_ptr) {
602 auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr)); 586 auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr));
603 if (!fp->Flush()) 587 if (!fp->Flush())
604 png_error(png_ptr, "Failed to flush to output PNG file."); 588 png_error(png_ptr, "Failed to flush to output PNG file.");
605} 589}
606#endif 590#endif
607 591
@@ -614,7 +598,8 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
614 598
615 // Write data to file 599 // Write data to file
616 static int dump_index = 0; 600 static int dump_index = 0;
617 std::string filename = std::string("texture_dump") + std::to_string(++dump_index) + std::string(".png"); 601 std::string filename =
602 std::string("texture_dump") + std::to_string(++dump_index) + std::string(".png");
618 u32 row_stride = texture_config.width * 3; 603 u32 row_stride = texture_config.width * 3;
619 604
620 u8* buf; 605 u8* buf;
@@ -632,7 +617,6 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
632 if (png_ptr == nullptr) { 617 if (png_ptr == nullptr) {
633 LOG_ERROR(Debug_GPU, "Could not allocate write struct"); 618 LOG_ERROR(Debug_GPU, "Could not allocate write struct");
634 goto finalise; 619 goto finalise;
635
636 } 620 }
637 621
638 // Initialize info structure 622 // Initialize info structure
@@ -651,9 +635,9 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
651 png_set_write_fn(png_ptr, static_cast<void*>(&fp), WriteIOFile, FlushIOFile); 635 png_set_write_fn(png_ptr, static_cast<void*>(&fp), WriteIOFile, FlushIOFile);
652 636
653 // Write header (8 bit color depth) 637 // Write header (8 bit color depth)
654 png_set_IHDR(png_ptr, info_ptr, texture_config.width, texture_config.height, 638 png_set_IHDR(png_ptr, info_ptr, texture_config.width, texture_config.height, 8,
655 8, PNG_COLOR_TYPE_RGB /*_ALPHA*/, PNG_INTERLACE_NONE, 639 PNG_COLOR_TYPE_RGB /*_ALPHA*/, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
656 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 640 PNG_FILTER_TYPE_BASE);
657 641
658 png_text title_text; 642 png_text title_text;
659 title_text.compression = PNG_TEXT_COMPRESSION_NONE; 643 title_text.compression = PNG_TEXT_COMPRESSION_NONE;
@@ -672,15 +656,14 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
672 info.stride = row_stride; 656 info.stride = row_stride;
673 info.format = g_state.regs.texture0_format; 657 info.format = g_state.regs.texture0_format;
674 Math::Vec4<u8> texture_color = LookupTexture(data, x, y, info); 658 Math::Vec4<u8> texture_color = LookupTexture(data, x, y, info);
675 buf[3 * x + y * row_stride ] = texture_color.r(); 659 buf[3 * x + y * row_stride] = texture_color.r();
676 buf[3 * x + y * row_stride + 1] = texture_color.g(); 660 buf[3 * x + y * row_stride + 1] = texture_color.g();
677 buf[3 * x + y * row_stride + 2] = texture_color.b(); 661 buf[3 * x + y * row_stride + 2] = texture_color.b();
678 } 662 }
679 } 663 }
680 664
681 // Write image data 665 // Write image data
682 for (unsigned y = 0; y < texture_config.height; ++y) 666 for (unsigned y = 0; y < texture_config.height; ++y) {
683 {
684 u8* row_ptr = (u8*)buf + y * row_stride; 667 u8* row_ptr = (u8*)buf + y * row_stride;
685 png_write_row(png_ptr, row_ptr); 668 png_write_row(png_ptr, row_ptr);
686 } 669 }
@@ -691,12 +674,15 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
691 png_write_end(png_ptr, nullptr); 674 png_write_end(png_ptr, nullptr);
692 675
693finalise: 676finalise:
694 if (info_ptr != nullptr) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); 677 if (info_ptr != nullptr)
695 if (png_ptr != nullptr) png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); 678 png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
679 if (png_ptr != nullptr)
680 png_destroy_write_struct(&png_ptr, (png_infopp) nullptr);
696#endif 681#endif
697} 682}
698 683
699static std::string ReplacePattern(const std::string& input, const std::string& pattern, const std::string& replacement) { 684static std::string ReplacePattern(const std::string& input, const std::string& pattern,
685 const std::string& replacement) {
700 size_t start = input.find(pattern); 686 size_t start = input.find(pattern);
701 if (start == std::string::npos) 687 if (start == std::string::npos)
702 return input; 688 return input;
@@ -709,16 +695,16 @@ static std::string ReplacePattern(const std::string& input, const std::string& p
709static std::string GetTevStageConfigSourceString(const Pica::Regs::TevStageConfig::Source& source) { 695static std::string GetTevStageConfigSourceString(const Pica::Regs::TevStageConfig::Source& source) {
710 using Source = Pica::Regs::TevStageConfig::Source; 696 using Source = Pica::Regs::TevStageConfig::Source;
711 static const std::map<Source, std::string> source_map = { 697 static const std::map<Source, std::string> source_map = {
712 { Source::PrimaryColor, "PrimaryColor" }, 698 {Source::PrimaryColor, "PrimaryColor"},
713 { Source::PrimaryFragmentColor, "PrimaryFragmentColor" }, 699 {Source::PrimaryFragmentColor, "PrimaryFragmentColor"},
714 { Source::SecondaryFragmentColor, "SecondaryFragmentColor" }, 700 {Source::SecondaryFragmentColor, "SecondaryFragmentColor"},
715 { Source::Texture0, "Texture0" }, 701 {Source::Texture0, "Texture0"},
716 { Source::Texture1, "Texture1" }, 702 {Source::Texture1, "Texture1"},
717 { Source::Texture2, "Texture2" }, 703 {Source::Texture2, "Texture2"},
718 { Source::Texture3, "Texture3" }, 704 {Source::Texture3, "Texture3"},
719 { Source::PreviousBuffer, "PreviousBuffer" }, 705 {Source::PreviousBuffer, "PreviousBuffer"},
720 { Source::Constant, "Constant" }, 706 {Source::Constant, "Constant"},
721 { Source::Previous, "Previous" }, 707 {Source::Previous, "Previous"},
722 }; 708 };
723 709
724 const auto src_it = source_map.find(source); 710 const auto src_it = source_map.find(source);
@@ -728,19 +714,21 @@ static std::string GetTevStageConfigSourceString(const Pica::Regs::TevStageConfi
728 return src_it->second; 714 return src_it->second;
729} 715}
730 716
731static std::string GetTevStageConfigColorSourceString(const Pica::Regs::TevStageConfig::Source& source, const Pica::Regs::TevStageConfig::ColorModifier modifier) { 717static std::string GetTevStageConfigColorSourceString(
718 const Pica::Regs::TevStageConfig::Source& source,
719 const Pica::Regs::TevStageConfig::ColorModifier modifier) {
732 using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier; 720 using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier;
733 static const std::map<ColorModifier, std::string> color_modifier_map = { 721 static const std::map<ColorModifier, std::string> color_modifier_map = {
734 { ColorModifier::SourceColor, "%source.rgb" }, 722 {ColorModifier::SourceColor, "%source.rgb"},
735 { ColorModifier::OneMinusSourceColor, "(1.0 - %source.rgb)" }, 723 {ColorModifier::OneMinusSourceColor, "(1.0 - %source.rgb)"},
736 { ColorModifier::SourceAlpha, "%source.aaa" }, 724 {ColorModifier::SourceAlpha, "%source.aaa"},
737 { ColorModifier::OneMinusSourceAlpha, "(1.0 - %source.aaa)" }, 725 {ColorModifier::OneMinusSourceAlpha, "(1.0 - %source.aaa)"},
738 { ColorModifier::SourceRed, "%source.rrr" }, 726 {ColorModifier::SourceRed, "%source.rrr"},
739 { ColorModifier::OneMinusSourceRed, "(1.0 - %source.rrr)" }, 727 {ColorModifier::OneMinusSourceRed, "(1.0 - %source.rrr)"},
740 { ColorModifier::SourceGreen, "%source.ggg" }, 728 {ColorModifier::SourceGreen, "%source.ggg"},
741 { ColorModifier::OneMinusSourceGreen, "(1.0 - %source.ggg)" }, 729 {ColorModifier::OneMinusSourceGreen, "(1.0 - %source.ggg)"},
742 { ColorModifier::SourceBlue, "%source.bbb" }, 730 {ColorModifier::SourceBlue, "%source.bbb"},
743 { ColorModifier::OneMinusSourceBlue, "(1.0 - %source.bbb)" }, 731 {ColorModifier::OneMinusSourceBlue, "(1.0 - %source.bbb)"},
744 }; 732 };
745 733
746 auto src_str = GetTevStageConfigSourceString(source); 734 auto src_str = GetTevStageConfigSourceString(source);
@@ -752,17 +740,19 @@ static std::string GetTevStageConfigColorSourceString(const Pica::Regs::TevStage
752 return ReplacePattern(modifier_str, "%source", src_str); 740 return ReplacePattern(modifier_str, "%source", src_str);
753} 741}
754 742
755static std::string GetTevStageConfigAlphaSourceString(const Pica::Regs::TevStageConfig::Source& source, const Pica::Regs::TevStageConfig::AlphaModifier modifier) { 743static std::string GetTevStageConfigAlphaSourceString(
744 const Pica::Regs::TevStageConfig::Source& source,
745 const Pica::Regs::TevStageConfig::AlphaModifier modifier) {
756 using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier; 746 using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier;
757 static const std::map<AlphaModifier, std::string> alpha_modifier_map = { 747 static const std::map<AlphaModifier, std::string> alpha_modifier_map = {
758 { AlphaModifier::SourceAlpha, "%source.a" }, 748 {AlphaModifier::SourceAlpha, "%source.a"},
759 { AlphaModifier::OneMinusSourceAlpha, "(1.0 - %source.a)" }, 749 {AlphaModifier::OneMinusSourceAlpha, "(1.0 - %source.a)"},
760 { AlphaModifier::SourceRed, "%source.r" }, 750 {AlphaModifier::SourceRed, "%source.r"},
761 { AlphaModifier::OneMinusSourceRed, "(1.0 - %source.r)" }, 751 {AlphaModifier::OneMinusSourceRed, "(1.0 - %source.r)"},
762 { AlphaModifier::SourceGreen, "%source.g" }, 752 {AlphaModifier::SourceGreen, "%source.g"},
763 { AlphaModifier::OneMinusSourceGreen, "(1.0 - %source.g)" }, 753 {AlphaModifier::OneMinusSourceGreen, "(1.0 - %source.g)"},
764 { AlphaModifier::SourceBlue, "%source.b" }, 754 {AlphaModifier::SourceBlue, "%source.b"},
765 { AlphaModifier::OneMinusSourceBlue, "(1.0 - %source.b)" }, 755 {AlphaModifier::OneMinusSourceBlue, "(1.0 - %source.b)"},
766 }; 756 };
767 757
768 auto src_str = GetTevStageConfigSourceString(source); 758 auto src_str = GetTevStageConfigSourceString(source);
@@ -774,18 +764,19 @@ static std::string GetTevStageConfigAlphaSourceString(const Pica::Regs::TevStage
774 return ReplacePattern(modifier_str, "%source", src_str); 764 return ReplacePattern(modifier_str, "%source", src_str);
775} 765}
776 766
777static std::string GetTevStageConfigOperationString(const Pica::Regs::TevStageConfig::Operation& operation) { 767static std::string GetTevStageConfigOperationString(
768 const Pica::Regs::TevStageConfig::Operation& operation) {
778 using Operation = Pica::Regs::TevStageConfig::Operation; 769 using Operation = Pica::Regs::TevStageConfig::Operation;
779 static const std::map<Operation, std::string> combiner_map = { 770 static const std::map<Operation, std::string> combiner_map = {
780 { Operation::Replace, "%source1" }, 771 {Operation::Replace, "%source1"},
781 { Operation::Modulate, "(%source1 * %source2)" }, 772 {Operation::Modulate, "(%source1 * %source2)"},
782 { Operation::Add, "(%source1 + %source2)" }, 773 {Operation::Add, "(%source1 + %source2)"},
783 { Operation::AddSigned, "(%source1 + %source2) - 0.5" }, 774 {Operation::AddSigned, "(%source1 + %source2) - 0.5"},
784 { Operation::Lerp, "lerp(%source1, %source2, %source3)" }, 775 {Operation::Lerp, "lerp(%source1, %source2, %source3)"},
785 { Operation::Subtract, "(%source1 - %source2)" }, 776 {Operation::Subtract, "(%source1 - %source2)"},
786 { Operation::Dot3_RGB, "dot(%source1, %source2)" }, 777 {Operation::Dot3_RGB, "dot(%source1, %source2)"},
787 { Operation::MultiplyThenAdd, "((%source1 * %source2) + %source3)" }, 778 {Operation::MultiplyThenAdd, "((%source1 * %source2) + %source3)"},
788 { Operation::AddThenMultiply, "((%source1 + %source2) * %source3)" }, 779 {Operation::AddThenMultiply, "((%source1 + %source2) * %source3)"},
789 }; 780 };
790 781
791 const auto op_it = combiner_map.find(operation); 782 const auto op_it = combiner_map.find(operation);
@@ -797,23 +788,37 @@ static std::string GetTevStageConfigOperationString(const Pica::Regs::TevStageCo
797 788
798std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage) { 789std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage) {
799 auto op_str = GetTevStageConfigOperationString(tev_stage.color_op); 790 auto op_str = GetTevStageConfigOperationString(tev_stage.color_op);
800 op_str = ReplacePattern(op_str, "%source1", GetTevStageConfigColorSourceString(tev_stage.color_source1, tev_stage.color_modifier1)); 791 op_str = ReplacePattern(
801 op_str = ReplacePattern(op_str, "%source2", GetTevStageConfigColorSourceString(tev_stage.color_source2, tev_stage.color_modifier2)); 792 op_str, "%source1",
802 return ReplacePattern(op_str, "%source3", GetTevStageConfigColorSourceString(tev_stage.color_source3, tev_stage.color_modifier3)); 793 GetTevStageConfigColorSourceString(tev_stage.color_source1, tev_stage.color_modifier1));
794 op_str = ReplacePattern(
795 op_str, "%source2",
796 GetTevStageConfigColorSourceString(tev_stage.color_source2, tev_stage.color_modifier2));
797 return ReplacePattern(
798 op_str, "%source3",
799 GetTevStageConfigColorSourceString(tev_stage.color_source3, tev_stage.color_modifier3));
803} 800}
804 801
805std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage) { 802std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage) {
806 auto op_str = GetTevStageConfigOperationString(tev_stage.alpha_op); 803 auto op_str = GetTevStageConfigOperationString(tev_stage.alpha_op);
807 op_str = ReplacePattern(op_str, "%source1", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source1, tev_stage.alpha_modifier1)); 804 op_str = ReplacePattern(
808 op_str = ReplacePattern(op_str, "%source2", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source2, tev_stage.alpha_modifier2)); 805 op_str, "%source1",
809 return ReplacePattern(op_str, "%source3", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source3, tev_stage.alpha_modifier3)); 806 GetTevStageConfigAlphaSourceString(tev_stage.alpha_source1, tev_stage.alpha_modifier1));
807 op_str = ReplacePattern(
808 op_str, "%source2",
809 GetTevStageConfigAlphaSourceString(tev_stage.alpha_source2, tev_stage.alpha_modifier2));
810 return ReplacePattern(
811 op_str, "%source3",
812 GetTevStageConfigAlphaSourceString(tev_stage.alpha_source3, tev_stage.alpha_modifier3));
810} 813}
811 814
812void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages) { 815void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages) {
813 std::string stage_info = "Tev setup:\n"; 816 std::string stage_info = "Tev setup:\n";
814 for (size_t index = 0; index < stages.size(); ++index) { 817 for (size_t index = 0; index < stages.size(); ++index) {
815 const auto& tev_stage = stages[index]; 818 const auto& tev_stage = stages[index];
816 stage_info += "Stage " + std::to_string(index) + ": " + GetTevStageConfigColorCombinerString(tev_stage) + " " + GetTevStageConfigAlphaCombinerString(tev_stage) + "\n"; 819 stage_info += "Stage " + std::to_string(index) + ": " +
820 GetTevStageConfigColorCombinerString(tev_stage) + " " +
821 GetTevStageConfigAlphaCombinerString(tev_stage) + "\n";
817 } 822 }
818 LOG_TRACE(HW_GPU, "%s", stage_info.c_str()); 823 LOG_TRACE(HW_GPU, "%s", stage_info.c_str());
819} 824}
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index 92e9734ae..189c93abb 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -15,10 +15,8 @@
15#include <string> 15#include <string>
16#include <utility> 16#include <utility>
17#include <vector> 17#include <vector>
18
19#include "common/common_types.h" 18#include "common/common_types.h"
20#include "common/vector_math.h" 19#include "common/vector_math.h"
21
22#include "video_core/pica.h" 20#include "video_core/pica.h"
23 21
24namespace CiTrace { 22namespace CiTrace {
@@ -53,13 +51,16 @@ public:
53 * Most importantly this is used for our debugger GUI. 51 * Most importantly this is used for our debugger GUI.
54 * 52 *
55 * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods. 53 * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods.
56 * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state access 54 * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
57 * @todo Evaluate an alternative interface, in which there is only one managing observer and multiple child observers running (by design) on the same thread. 55 * access
56 * @todo Evaluate an alternative interface, in which there is only one managing observer and
57 * multiple child observers running (by design) on the same thread.
58 */ 58 */
59 class BreakPointObserver { 59 class BreakPointObserver {
60 public: 60 public:
61 /// Constructs the object such that it observes events of the given DebugContext. 61 /// Constructs the object such that it observes events of the given DebugContext.
62 BreakPointObserver(std::shared_ptr<DebugContext> debug_context) : context_weak(debug_context) { 62 BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
63 : context_weak(debug_context) {
63 std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex); 64 std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
64 debug_context->breakpoint_observers.push_back(this); 65 debug_context->breakpoint_observers.push_back(this);
65 } 66 }
@@ -84,15 +85,13 @@ public:
84 * @param data Optional data pointer (if unused, this is a nullptr) 85 * @param data Optional data pointer (if unused, this is a nullptr)
85 * @note This function will perform nothing unless it is overridden in the child class. 86 * @note This function will perform nothing unless it is overridden in the child class.
86 */ 87 */
87 virtual void OnPicaBreakPointHit(Event, void*) { 88 virtual void OnPicaBreakPointHit(Event, void*) {}
88 }
89 89
90 /** 90 /**
91 * Action to perform when emulation is resumed from a breakpoint. 91 * Action to perform when emulation is resumed from a breakpoint.
92 * @note This function will perform nothing unless it is overridden in the child class. 92 * @note This function will perform nothing unless it is overridden in the child class.
93 */ 93 */
94 virtual void OnPicaResume() { 94 virtual void OnPicaResume() {}
95 }
96 95
97 protected: 96 protected:
98 /** 97 /**
@@ -122,7 +121,8 @@ public:
122 * The current thread then is halted until Resume() is called from another thread (or until 121 * The current thread then is halted until Resume() is called from another thread (or until
123 * emulation is stopped). 122 * emulation is stopped).
124 * @param event Event which has happened 123 * @param event Event which has happened
125 * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called. 124 * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
125 * Resume() is called.
126 */ 126 */
127 void OnEvent(Event event, void* data) { 127 void OnEvent(Event event, void* data) {
128 // This check is left in the header to allow the compiler to inline it. 128 // This check is left in the header to allow the compiler to inline it.
@@ -132,11 +132,12 @@ public:
132 DoOnEvent(event, data); 132 DoOnEvent(event, data);
133 } 133 }
134 134
135 void DoOnEvent(Event event, void *data); 135 void DoOnEvent(Event event, void* data);
136 136
137 /** 137 /**
138 * Resume from the current breakpoint. 138 * Resume from the current breakpoint.
139 * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. Calling from any other thread is safe. 139 * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
140 * Calling from any other thread is safe.
140 */ 141 */
141 void Resume(); 142 void Resume();
142 143
@@ -144,7 +145,7 @@ public:
144 * Delete all set breakpoints and resume emulation. 145 * Delete all set breakpoints and resume emulation.
145 */ 146 */
146 void ClearBreakpoints() { 147 void ClearBreakpoints() {
147 for (auto &bp : breakpoints) { 148 for (auto& bp : breakpoints) {
148 bp.enabled = false; 149 bp.enabled = false;
149 } 150 }
150 Resume(); 151 Resume();
@@ -182,8 +183,8 @@ namespace DebugUtils {
182#define PICA_LOG_TEV 0 183#define PICA_LOG_TEV 0
183 184
184void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, 185void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
185 const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes); 186 const Shader::ShaderSetup& setup,
186 187 const Regs::VSOutputAttributes* output_attributes);
187 188
188// Utility class to log Pica commands. 189// Utility class to log Pica commands.
189struct PicaTrace { 190struct PicaTrace {
@@ -216,7 +217,10 @@ struct TextureInfo {
216 * @param source Source pointer to read data from 217 * @param source Source pointer to read data from
217 * @param s,t Texture coordinates to read from 218 * @param s,t Texture coordinates to read from
218 * @param info TextureInfo object describing the texture setup 219 * @param info TextureInfo object describing the texture setup
219 * @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. 220 * @param disable_alpha This is used for debug widgets which use this method to display textures
221 * without providing a good way to visualize alpha by themselves. If true, this will return 255 for
222 * the alpha component, and either drop the information entirely or store it in an "unused" color
223 * channel.
220 * @todo Eventually we should get rid of the disable_alpha parameter. 224 * @todo Eventually we should get rid of the disable_alpha parameter.
221 */ 225 */
222const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info, 226const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info,
@@ -237,7 +241,8 @@ class MemoryAccessTracker {
237 /// Combine overlapping and close ranges 241 /// Combine overlapping and close ranges
238 void SimplifyRanges() { 242 void SimplifyRanges() {
239 for (auto it = ranges.begin(); it != ranges.end(); ++it) { 243 for (auto it = ranges.begin(); it != ranges.end(); ++it) {
240 // NOTE: We add 32 to the range end address to make sure "close" ranges are combined, too 244 // NOTE: We add 32 to the range end address to make sure "close" ranges are combined,
245 // too
241 auto it2 = std::next(it); 246 auto it2 = std::next(it);
242 while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) { 247 while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) {
243 it->second = std::max(it->second, it2->first + it2->second - it->first); 248 it->second = std::max(it->second, it2->first + it2->second - it->first);