/ src / video_core / rasterizer_accelerated.cpp
rasterizer_accelerated.cpp
  1  // Copyright 2023 Citra Emulator Project
  2  // Licensed under GPLv2 or any later version
  3  // Refer to the license.txt file included.
  4  
  5  #include "common/alignment.h"
  6  #include "core/memory.h"
  7  #include "video_core/pica/pica_core.h"
  8  #include "video_core/rasterizer_accelerated.h"
  9  
 10  namespace VideoCore {
 11  
 12  using Pica::f24;
 13  
 14  static Common::Vec4f ColorRGBA8(const u32 color) {
 15      const auto rgba =
 16          Common::Vec4u{color >> 0 & 0xFF, color >> 8 & 0xFF, color >> 16 & 0xFF, color >> 24 & 0xFF};
 17      return rgba / 255.0f;
 18  }
 19  
 20  static Common::Vec3f LightColor(const Pica::LightingRegs::LightColor& color) {
 21      return Common::Vec3u{color.r, color.g, color.b} / 255.0f;
 22  }
 23  
 24  RasterizerAccelerated::HardwareVertex::HardwareVertex(const Pica::OutputVertex& v,
 25                                                        bool flip_quaternion) {
 26      position[0] = v.pos.x.ToFloat32();
 27      position[1] = v.pos.y.ToFloat32();
 28      position[2] = v.pos.z.ToFloat32();
 29      position[3] = v.pos.w.ToFloat32();
 30      color[0] = v.color.x.ToFloat32();
 31      color[1] = v.color.y.ToFloat32();
 32      color[2] = v.color.z.ToFloat32();
 33      color[3] = v.color.w.ToFloat32();
 34      tex_coord0[0] = v.tc0.x.ToFloat32();
 35      tex_coord0[1] = v.tc0.y.ToFloat32();
 36      tex_coord1[0] = v.tc1.x.ToFloat32();
 37      tex_coord1[1] = v.tc1.y.ToFloat32();
 38      tex_coord2[0] = v.tc2.x.ToFloat32();
 39      tex_coord2[1] = v.tc2.y.ToFloat32();
 40      tex_coord0_w = v.tc0_w.ToFloat32();
 41      normquat[0] = v.quat.x.ToFloat32();
 42      normquat[1] = v.quat.y.ToFloat32();
 43      normquat[2] = v.quat.z.ToFloat32();
 44      normquat[3] = v.quat.w.ToFloat32();
 45      view[0] = v.view.x.ToFloat32();
 46      view[1] = v.view.y.ToFloat32();
 47      view[2] = v.view.z.ToFloat32();
 48  
 49      if (flip_quaternion) {
 50          normquat = -normquat;
 51      }
 52  }
 53  
 54  RasterizerAccelerated::RasterizerAccelerated(Memory::MemorySystem& memory_, Pica::PicaCore& pica_)
 55      : memory{memory_}, pica{pica_}, regs{pica.regs.internal} {
 56      fs_uniform_block_data.lighting_lut_dirty.fill(true);
 57  }
 58  
 59  /**
 60   * This is a helper function to resolve an issue when interpolating opposite quaternions. See below
 61   * for a detailed description of this issue (yuriks):
 62   *
 63   * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you
 64   * interpolate two quaternions that are opposite, instead of going from one rotation to another
 65   * using the shortest path, you'll go around the longest path. You can test if two quaternions are
 66   * opposite by checking if Dot(Q1, Q2) < 0. In that case, you can flip either of them, therefore
 67   * making Dot(Q1, -Q2) positive.
 68   *
 69   * This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This is
 70   * correct for most cases but can still rotate around the long way sometimes. An implementation
 71   * which did `lerp(lerp(Q1, Q2), Q3)` (with proper weighting), applying the dot product check
 72   * between each step would work for those cases at the cost of being more complex to implement.
 73   *
 74   * Fortunately however, the 3DS hardware happens to also use this exact same logic to work around
 75   * these issues, making this basic implementation actually more accurate to the hardware.
 76   */
 77  static bool AreQuaternionsOpposite(Common::Vec4<f24> qa, Common::Vec4<f24> qb) {
 78      Common::Vec4f a{qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32()};
 79      Common::Vec4f b{qb.x.ToFloat32(), qb.y.ToFloat32(), qb.z.ToFloat32(), qb.w.ToFloat32()};
 80  
 81      return (Common::Dot(a, b) < 0.f);
 82  }
 83  
 84  void RasterizerAccelerated::AddTriangle(const Pica::OutputVertex& v0, const Pica::OutputVertex& v1,
 85                                          const Pica::OutputVertex& v2) {
 86      vertex_batch.emplace_back(v0, false);
 87      vertex_batch.emplace_back(v1, AreQuaternionsOpposite(v0.quat, v1.quat));
 88      vertex_batch.emplace_back(v2, AreQuaternionsOpposite(v0.quat, v2.quat));
 89  }
 90  
 91  RasterizerAccelerated::VertexArrayInfo RasterizerAccelerated::AnalyzeVertexArray(
 92      bool is_indexed, u32 stride_alignment) {
 93      const auto& vertex_attributes = regs.pipeline.vertex_attributes;
 94  
 95      u32 vertex_min;
 96      u32 vertex_max;
 97      if (is_indexed) {
 98          const auto& index_info = regs.pipeline.index_array;
 99          const PAddr address = vertex_attributes.GetPhysicalBaseAddress() + index_info.offset;
100          const u8* index_address_8 = memory.GetPhysicalPointer(address);
101          const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8);
102          const bool index_u16 = index_info.format != 0;
103  
104          vertex_min = 0xFFFF;
105          vertex_max = 0;
106          const u32 size = regs.pipeline.num_vertices * (index_u16 ? 2 : 1);
107          FlushRegion(address, size);
108          for (u32 index = 0; index < regs.pipeline.num_vertices; ++index) {
109              const u32 vertex = index_u16 ? index_address_16[index] : index_address_8[index];
110              vertex_min = std::min(vertex_min, vertex);
111              vertex_max = std::max(vertex_max, vertex);
112          }
113      } else {
114          vertex_min = regs.pipeline.vertex_offset;
115          vertex_max = regs.pipeline.vertex_offset + regs.pipeline.num_vertices - 1;
116      }
117  
118      const u32 vertex_num = vertex_max - vertex_min + 1;
119      u32 vs_input_size = 0;
120      for (const auto& loader : vertex_attributes.attribute_loaders) {
121          if (loader.component_count != 0) {
122              const u32 aligned_stride =
123                  Common::AlignUp(static_cast<u32>(loader.byte_count), stride_alignment);
124              vs_input_size += Common::AlignUp(aligned_stride * vertex_num, 4);
125          }
126      }
127  
128      return {vertex_min, vertex_max, vs_input_size};
129  }
130  
131  void RasterizerAccelerated::SyncEntireState() {
132      // Sync renderer-specific fixed-function state
133      SyncFixedState();
134  
135      // Sync uniforms
136      SyncClipPlane();
137      SyncDepthScale();
138      SyncDepthOffset();
139      SyncAlphaTest();
140      SyncCombinerColor();
141      auto& tev_stages = regs.texturing.GetTevStages();
142      for (std::size_t index = 0; index < tev_stages.size(); ++index) {
143          SyncTevConstColor(index, tev_stages[index]);
144      }
145  
146      SyncGlobalAmbient();
147      for (u32 light_index = 0; light_index < 8; light_index++) {
148          SyncLightSpecular0(light_index);
149          SyncLightSpecular1(light_index);
150          SyncLightDiffuse(light_index);
151          SyncLightAmbient(light_index);
152          SyncLightPosition(light_index);
153          SyncLightDistanceAttenuationBias(light_index);
154          SyncLightDistanceAttenuationScale(light_index);
155      }
156  
157      SyncFogColor();
158      SyncProcTexNoise();
159      SyncProcTexBias();
160      SyncShadowBias();
161      SyncShadowTextureBias();
162  
163      for (u32 tex_index = 0; tex_index < 3; tex_index++) {
164          SyncTextureLodBias(tex_index);
165      }
166  }
167  
168  void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
169      switch (id) {
170      // Depth modifiers
171      case PICA_REG_INDEX(rasterizer.viewport_depth_range):
172          SyncDepthScale();
173          break;
174      case PICA_REG_INDEX(rasterizer.viewport_depth_near_plane):
175          SyncDepthOffset();
176          break;
177  
178      // Depth buffering
179      case PICA_REG_INDEX(rasterizer.depthmap_enable):
180          shader_dirty = true;
181          break;
182  
183      // Shadow texture
184      case PICA_REG_INDEX(texturing.shadow):
185          SyncShadowTextureBias();
186          break;
187  
188      // Fog state
189      case PICA_REG_INDEX(texturing.fog_color):
190          SyncFogColor();
191          break;
192      case PICA_REG_INDEX(texturing.fog_lut_data[0]):
193      case PICA_REG_INDEX(texturing.fog_lut_data[1]):
194      case PICA_REG_INDEX(texturing.fog_lut_data[2]):
195      case PICA_REG_INDEX(texturing.fog_lut_data[3]):
196      case PICA_REG_INDEX(texturing.fog_lut_data[4]):
197      case PICA_REG_INDEX(texturing.fog_lut_data[5]):
198      case PICA_REG_INDEX(texturing.fog_lut_data[6]):
199      case PICA_REG_INDEX(texturing.fog_lut_data[7]):
200          fs_uniform_block_data.fog_lut_dirty = true;
201          break;
202  
203      // ProcTex state
204      case PICA_REG_INDEX(texturing.proctex):
205      case PICA_REG_INDEX(texturing.proctex_lut):
206      case PICA_REG_INDEX(texturing.proctex_lut_offset):
207          SyncProcTexBias();
208          shader_dirty = true;
209          break;
210  
211      case PICA_REG_INDEX(texturing.proctex_noise_u):
212      case PICA_REG_INDEX(texturing.proctex_noise_v):
213      case PICA_REG_INDEX(texturing.proctex_noise_frequency):
214          SyncProcTexNoise();
215          break;
216  
217      case PICA_REG_INDEX(texturing.proctex_lut_data[0]):
218      case PICA_REG_INDEX(texturing.proctex_lut_data[1]):
219      case PICA_REG_INDEX(texturing.proctex_lut_data[2]):
220      case PICA_REG_INDEX(texturing.proctex_lut_data[3]):
221      case PICA_REG_INDEX(texturing.proctex_lut_data[4]):
222      case PICA_REG_INDEX(texturing.proctex_lut_data[5]):
223      case PICA_REG_INDEX(texturing.proctex_lut_data[6]):
224      case PICA_REG_INDEX(texturing.proctex_lut_data[7]):
225          using Pica::TexturingRegs;
226          switch (regs.texturing.proctex_lut_config.ref_table.Value()) {
227          case TexturingRegs::ProcTexLutTable::Noise:
228              fs_uniform_block_data.proctex_noise_lut_dirty = true;
229              break;
230          case TexturingRegs::ProcTexLutTable::ColorMap:
231              fs_uniform_block_data.proctex_color_map_dirty = true;
232              break;
233          case TexturingRegs::ProcTexLutTable::AlphaMap:
234              fs_uniform_block_data.proctex_alpha_map_dirty = true;
235              break;
236          case TexturingRegs::ProcTexLutTable::Color:
237              fs_uniform_block_data.proctex_lut_dirty = true;
238              break;
239          case TexturingRegs::ProcTexLutTable::ColorDiff:
240              fs_uniform_block_data.proctex_diff_lut_dirty = true;
241              break;
242          }
243          break;
244  
245      // Fragment operation mode
246      case PICA_REG_INDEX(framebuffer.output_merger.fragment_operation_mode):
247          shader_dirty = true;
248          break;
249  
250      // Alpha test
251      case PICA_REG_INDEX(framebuffer.output_merger.alpha_test):
252          SyncAlphaTest();
253          shader_dirty = true;
254          break;
255  
256      case PICA_REG_INDEX(framebuffer.shadow):
257          SyncShadowBias();
258          break;
259  
260      // Scissor test
261      case PICA_REG_INDEX(rasterizer.scissor_test.mode):
262          shader_dirty = true;
263          break;
264  
265      case PICA_REG_INDEX(texturing.main_config):
266          shader_dirty = true;
267          break;
268  
269      // Texture 0 type
270      case PICA_REG_INDEX(texturing.texture0.type):
271          shader_dirty = true;
272          break;
273  
274      // TEV stages
275      // (This also syncs fog_mode and fog_flip which are part of tev_combiner_buffer_input)
276      case PICA_REG_INDEX(texturing.tev_stage0.color_source1):
277      case PICA_REG_INDEX(texturing.tev_stage0.color_modifier1):
278      case PICA_REG_INDEX(texturing.tev_stage0.color_op):
279      case PICA_REG_INDEX(texturing.tev_stage0.color_scale):
280      case PICA_REG_INDEX(texturing.tev_stage1.color_source1):
281      case PICA_REG_INDEX(texturing.tev_stage1.color_modifier1):
282      case PICA_REG_INDEX(texturing.tev_stage1.color_op):
283      case PICA_REG_INDEX(texturing.tev_stage1.color_scale):
284      case PICA_REG_INDEX(texturing.tev_stage2.color_source1):
285      case PICA_REG_INDEX(texturing.tev_stage2.color_modifier1):
286      case PICA_REG_INDEX(texturing.tev_stage2.color_op):
287      case PICA_REG_INDEX(texturing.tev_stage2.color_scale):
288      case PICA_REG_INDEX(texturing.tev_stage3.color_source1):
289      case PICA_REG_INDEX(texturing.tev_stage3.color_modifier1):
290      case PICA_REG_INDEX(texturing.tev_stage3.color_op):
291      case PICA_REG_INDEX(texturing.tev_stage3.color_scale):
292      case PICA_REG_INDEX(texturing.tev_stage4.color_source1):
293      case PICA_REG_INDEX(texturing.tev_stage4.color_modifier1):
294      case PICA_REG_INDEX(texturing.tev_stage4.color_op):
295      case PICA_REG_INDEX(texturing.tev_stage4.color_scale):
296      case PICA_REG_INDEX(texturing.tev_stage5.color_source1):
297      case PICA_REG_INDEX(texturing.tev_stage5.color_modifier1):
298      case PICA_REG_INDEX(texturing.tev_stage5.color_op):
299      case PICA_REG_INDEX(texturing.tev_stage5.color_scale):
300      case PICA_REG_INDEX(texturing.tev_combiner_buffer_input):
301          shader_dirty = true;
302          break;
303      case PICA_REG_INDEX(texturing.tev_stage0.const_r):
304          SyncTevConstColor(0, regs.texturing.tev_stage0);
305          break;
306      case PICA_REG_INDEX(texturing.tev_stage1.const_r):
307          SyncTevConstColor(1, regs.texturing.tev_stage1);
308          break;
309      case PICA_REG_INDEX(texturing.tev_stage2.const_r):
310          SyncTevConstColor(2, regs.texturing.tev_stage2);
311          break;
312      case PICA_REG_INDEX(texturing.tev_stage3.const_r):
313          SyncTevConstColor(3, regs.texturing.tev_stage3);
314          break;
315      case PICA_REG_INDEX(texturing.tev_stage4.const_r):
316          SyncTevConstColor(4, regs.texturing.tev_stage4);
317          break;
318      case PICA_REG_INDEX(texturing.tev_stage5.const_r):
319          SyncTevConstColor(5, regs.texturing.tev_stage5);
320          break;
321  
322      // TEV combiner buffer color
323      case PICA_REG_INDEX(texturing.tev_combiner_buffer_color):
324          SyncCombinerColor();
325          break;
326  
327      // Fragment lighting switches
328      case PICA_REG_INDEX(lighting.disable):
329      case PICA_REG_INDEX(lighting.max_light_index):
330      case PICA_REG_INDEX(lighting.config0):
331      case PICA_REG_INDEX(lighting.config1):
332      case PICA_REG_INDEX(lighting.abs_lut_input):
333      case PICA_REG_INDEX(lighting.lut_input):
334      case PICA_REG_INDEX(lighting.lut_scale):
335      case PICA_REG_INDEX(lighting.light_enable):
336          break;
337  
338      // Fragment lighting specular 0 color
339      case PICA_REG_INDEX(lighting.light[0].specular_0):
340          SyncLightSpecular0(0);
341          break;
342      case PICA_REG_INDEX(lighting.light[1].specular_0):
343          SyncLightSpecular0(1);
344          break;
345      case PICA_REG_INDEX(lighting.light[2].specular_0):
346          SyncLightSpecular0(2);
347          break;
348      case PICA_REG_INDEX(lighting.light[3].specular_0):
349          SyncLightSpecular0(3);
350          break;
351      case PICA_REG_INDEX(lighting.light[4].specular_0):
352          SyncLightSpecular0(4);
353          break;
354      case PICA_REG_INDEX(lighting.light[5].specular_0):
355          SyncLightSpecular0(5);
356          break;
357      case PICA_REG_INDEX(lighting.light[6].specular_0):
358          SyncLightSpecular0(6);
359          break;
360      case PICA_REG_INDEX(lighting.light[7].specular_0):
361          SyncLightSpecular0(7);
362          break;
363  
364      // Fragment lighting specular 1 color
365      case PICA_REG_INDEX(lighting.light[0].specular_1):
366          SyncLightSpecular1(0);
367          break;
368      case PICA_REG_INDEX(lighting.light[1].specular_1):
369          SyncLightSpecular1(1);
370          break;
371      case PICA_REG_INDEX(lighting.light[2].specular_1):
372          SyncLightSpecular1(2);
373          break;
374      case PICA_REG_INDEX(lighting.light[3].specular_1):
375          SyncLightSpecular1(3);
376          break;
377      case PICA_REG_INDEX(lighting.light[4].specular_1):
378          SyncLightSpecular1(4);
379          break;
380      case PICA_REG_INDEX(lighting.light[5].specular_1):
381          SyncLightSpecular1(5);
382          break;
383      case PICA_REG_INDEX(lighting.light[6].specular_1):
384          SyncLightSpecular1(6);
385          break;
386      case PICA_REG_INDEX(lighting.light[7].specular_1):
387          SyncLightSpecular1(7);
388          break;
389  
390      // Fragment lighting diffuse color
391      case PICA_REG_INDEX(lighting.light[0].diffuse):
392          SyncLightDiffuse(0);
393          break;
394      case PICA_REG_INDEX(lighting.light[1].diffuse):
395          SyncLightDiffuse(1);
396          break;
397      case PICA_REG_INDEX(lighting.light[2].diffuse):
398          SyncLightDiffuse(2);
399          break;
400      case PICA_REG_INDEX(lighting.light[3].diffuse):
401          SyncLightDiffuse(3);
402          break;
403      case PICA_REG_INDEX(lighting.light[4].diffuse):
404          SyncLightDiffuse(4);
405          break;
406      case PICA_REG_INDEX(lighting.light[5].diffuse):
407          SyncLightDiffuse(5);
408          break;
409      case PICA_REG_INDEX(lighting.light[6].diffuse):
410          SyncLightDiffuse(6);
411          break;
412      case PICA_REG_INDEX(lighting.light[7].diffuse):
413          SyncLightDiffuse(7);
414          break;
415  
416      // Fragment lighting ambient color
417      case PICA_REG_INDEX(lighting.light[0].ambient):
418          SyncLightAmbient(0);
419          break;
420      case PICA_REG_INDEX(lighting.light[1].ambient):
421          SyncLightAmbient(1);
422          break;
423      case PICA_REG_INDEX(lighting.light[2].ambient):
424          SyncLightAmbient(2);
425          break;
426      case PICA_REG_INDEX(lighting.light[3].ambient):
427          SyncLightAmbient(3);
428          break;
429      case PICA_REG_INDEX(lighting.light[4].ambient):
430          SyncLightAmbient(4);
431          break;
432      case PICA_REG_INDEX(lighting.light[5].ambient):
433          SyncLightAmbient(5);
434          break;
435      case PICA_REG_INDEX(lighting.light[6].ambient):
436          SyncLightAmbient(6);
437          break;
438      case PICA_REG_INDEX(lighting.light[7].ambient):
439          SyncLightAmbient(7);
440          break;
441  
442      // Fragment lighting position
443      case PICA_REG_INDEX(lighting.light[0].x):
444      case PICA_REG_INDEX(lighting.light[0].z):
445          SyncLightPosition(0);
446          break;
447      case PICA_REG_INDEX(lighting.light[1].x):
448      case PICA_REG_INDEX(lighting.light[1].z):
449          SyncLightPosition(1);
450          break;
451      case PICA_REG_INDEX(lighting.light[2].x):
452      case PICA_REG_INDEX(lighting.light[2].z):
453          SyncLightPosition(2);
454          break;
455      case PICA_REG_INDEX(lighting.light[3].x):
456      case PICA_REG_INDEX(lighting.light[3].z):
457          SyncLightPosition(3);
458          break;
459      case PICA_REG_INDEX(lighting.light[4].x):
460      case PICA_REG_INDEX(lighting.light[4].z):
461          SyncLightPosition(4);
462          break;
463      case PICA_REG_INDEX(lighting.light[5].x):
464      case PICA_REG_INDEX(lighting.light[5].z):
465          SyncLightPosition(5);
466          break;
467      case PICA_REG_INDEX(lighting.light[6].x):
468      case PICA_REG_INDEX(lighting.light[6].z):
469          SyncLightPosition(6);
470          break;
471      case PICA_REG_INDEX(lighting.light[7].x):
472      case PICA_REG_INDEX(lighting.light[7].z):
473          SyncLightPosition(7);
474          break;
475  
476      // Fragment spot lighting direction
477      case PICA_REG_INDEX(lighting.light[0].spot_x):
478      case PICA_REG_INDEX(lighting.light[0].spot_z):
479          SyncLightSpotDirection(0);
480          break;
481      case PICA_REG_INDEX(lighting.light[1].spot_x):
482      case PICA_REG_INDEX(lighting.light[1].spot_z):
483          SyncLightSpotDirection(1);
484          break;
485      case PICA_REG_INDEX(lighting.light[2].spot_x):
486      case PICA_REG_INDEX(lighting.light[2].spot_z):
487          SyncLightSpotDirection(2);
488          break;
489      case PICA_REG_INDEX(lighting.light[3].spot_x):
490      case PICA_REG_INDEX(lighting.light[3].spot_z):
491          SyncLightSpotDirection(3);
492          break;
493      case PICA_REG_INDEX(lighting.light[4].spot_x):
494      case PICA_REG_INDEX(lighting.light[4].spot_z):
495          SyncLightSpotDirection(4);
496          break;
497      case PICA_REG_INDEX(lighting.light[5].spot_x):
498      case PICA_REG_INDEX(lighting.light[5].spot_z):
499          SyncLightSpotDirection(5);
500          break;
501      case PICA_REG_INDEX(lighting.light[6].spot_x):
502      case PICA_REG_INDEX(lighting.light[6].spot_z):
503          SyncLightSpotDirection(6);
504          break;
505      case PICA_REG_INDEX(lighting.light[7].spot_x):
506      case PICA_REG_INDEX(lighting.light[7].spot_z):
507          SyncLightSpotDirection(7);
508          break;
509  
510      // Fragment lighting light source config
511      case PICA_REG_INDEX(lighting.light[0].config):
512      case PICA_REG_INDEX(lighting.light[1].config):
513      case PICA_REG_INDEX(lighting.light[2].config):
514      case PICA_REG_INDEX(lighting.light[3].config):
515      case PICA_REG_INDEX(lighting.light[4].config):
516      case PICA_REG_INDEX(lighting.light[5].config):
517      case PICA_REG_INDEX(lighting.light[6].config):
518      case PICA_REG_INDEX(lighting.light[7].config):
519          shader_dirty = true;
520          break;
521  
522      // Fragment lighting distance attenuation bias
523      case PICA_REG_INDEX(lighting.light[0].dist_atten_bias):
524          SyncLightDistanceAttenuationBias(0);
525          break;
526      case PICA_REG_INDEX(lighting.light[1].dist_atten_bias):
527          SyncLightDistanceAttenuationBias(1);
528          break;
529      case PICA_REG_INDEX(lighting.light[2].dist_atten_bias):
530          SyncLightDistanceAttenuationBias(2);
531          break;
532      case PICA_REG_INDEX(lighting.light[3].dist_atten_bias):
533          SyncLightDistanceAttenuationBias(3);
534          break;
535      case PICA_REG_INDEX(lighting.light[4].dist_atten_bias):
536          SyncLightDistanceAttenuationBias(4);
537          break;
538      case PICA_REG_INDEX(lighting.light[5].dist_atten_bias):
539          SyncLightDistanceAttenuationBias(5);
540          break;
541      case PICA_REG_INDEX(lighting.light[6].dist_atten_bias):
542          SyncLightDistanceAttenuationBias(6);
543          break;
544      case PICA_REG_INDEX(lighting.light[7].dist_atten_bias):
545          SyncLightDistanceAttenuationBias(7);
546          break;
547  
548      // Fragment lighting distance attenuation scale
549      case PICA_REG_INDEX(lighting.light[0].dist_atten_scale):
550          SyncLightDistanceAttenuationScale(0);
551          break;
552      case PICA_REG_INDEX(lighting.light[1].dist_atten_scale):
553          SyncLightDistanceAttenuationScale(1);
554          break;
555      case PICA_REG_INDEX(lighting.light[2].dist_atten_scale):
556          SyncLightDistanceAttenuationScale(2);
557          break;
558      case PICA_REG_INDEX(lighting.light[3].dist_atten_scale):
559          SyncLightDistanceAttenuationScale(3);
560          break;
561      case PICA_REG_INDEX(lighting.light[4].dist_atten_scale):
562          SyncLightDistanceAttenuationScale(4);
563          break;
564      case PICA_REG_INDEX(lighting.light[5].dist_atten_scale):
565          SyncLightDistanceAttenuationScale(5);
566          break;
567      case PICA_REG_INDEX(lighting.light[6].dist_atten_scale):
568          SyncLightDistanceAttenuationScale(6);
569          break;
570      case PICA_REG_INDEX(lighting.light[7].dist_atten_scale):
571          SyncLightDistanceAttenuationScale(7);
572          break;
573  
574      // Fragment lighting global ambient color (emission + ambient * ambient)
575      case PICA_REG_INDEX(lighting.global_ambient):
576          SyncGlobalAmbient();
577          break;
578  
579      // Fragment lighting lookup tables
580      case PICA_REG_INDEX(lighting.lut_data[0]):
581      case PICA_REG_INDEX(lighting.lut_data[1]):
582      case PICA_REG_INDEX(lighting.lut_data[2]):
583      case PICA_REG_INDEX(lighting.lut_data[3]):
584      case PICA_REG_INDEX(lighting.lut_data[4]):
585      case PICA_REG_INDEX(lighting.lut_data[5]):
586      case PICA_REG_INDEX(lighting.lut_data[6]):
587      case PICA_REG_INDEX(lighting.lut_data[7]): {
588          const auto& lut_config = regs.lighting.lut_config;
589          fs_uniform_block_data.lighting_lut_dirty[lut_config.type] = true;
590          fs_uniform_block_data.lighting_lut_dirty_any = true;
591          break;
592      }
593  
594      // Texture LOD biases
595      case PICA_REG_INDEX(texturing.texture0.lod.bias):
596          SyncTextureLodBias(0);
597          break;
598      case PICA_REG_INDEX(texturing.texture1.lod.bias):
599          SyncTextureLodBias(1);
600          break;
601      case PICA_REG_INDEX(texturing.texture2.lod.bias):
602          SyncTextureLodBias(2);
603          break;
604  
605      // Texture borders
606      case PICA_REG_INDEX(texturing.texture0.border_color):
607          SyncTextureBorderColor(0);
608          break;
609      case PICA_REG_INDEX(texturing.texture1.border_color):
610          SyncTextureBorderColor(1);
611          break;
612      case PICA_REG_INDEX(texturing.texture2.border_color):
613          SyncTextureBorderColor(2);
614          break;
615  
616      // Clipping plane
617      case PICA_REG_INDEX(rasterizer.clip_enable):
618      case PICA_REG_INDEX(rasterizer.clip_coef[0]):
619      case PICA_REG_INDEX(rasterizer.clip_coef[1]):
620      case PICA_REG_INDEX(rasterizer.clip_coef[2]):
621      case PICA_REG_INDEX(rasterizer.clip_coef[3]):
622          SyncClipPlane();
623          break;
624      }
625  
626      // Forward registers that map to fixed function API features to the video backend
627      NotifyFixedFunctionPicaRegisterChanged(id);
628  }
629  
630  void RasterizerAccelerated::SyncDepthScale() {
631      const f32 depth_scale = f24::FromRaw(regs.rasterizer.viewport_depth_range).ToFloat32();
632  
633      if (depth_scale != fs_uniform_block_data.data.depth_scale) {
634          fs_uniform_block_data.data.depth_scale = depth_scale;
635          fs_uniform_block_data.dirty = true;
636      }
637  }
638  
639  void RasterizerAccelerated::SyncDepthOffset() {
640      const f32 depth_offset = f24::FromRaw(regs.rasterizer.viewport_depth_near_plane).ToFloat32();
641  
642      if (depth_offset != fs_uniform_block_data.data.depth_offset) {
643          fs_uniform_block_data.data.depth_offset = depth_offset;
644          fs_uniform_block_data.dirty = true;
645      }
646  }
647  
648  void RasterizerAccelerated::SyncFogColor() {
649      const auto& fog_color_regs = regs.texturing.fog_color;
650      const Common::Vec3f fog_color = {
651          fog_color_regs.r.Value() / 255.0f,
652          fog_color_regs.g.Value() / 255.0f,
653          fog_color_regs.b.Value() / 255.0f,
654      };
655  
656      if (fog_color != fs_uniform_block_data.data.fog_color) {
657          fs_uniform_block_data.data.fog_color = fog_color;
658          fs_uniform_block_data.dirty = true;
659      }
660  }
661  
662  void RasterizerAccelerated::SyncProcTexNoise() {
663      const Common::Vec2f proctex_noise_f = {
664          Pica::f16::FromRaw(regs.texturing.proctex_noise_frequency.u).ToFloat32(),
665          Pica::f16::FromRaw(regs.texturing.proctex_noise_frequency.v).ToFloat32(),
666      };
667      const Common::Vec2f proctex_noise_a = {
668          regs.texturing.proctex_noise_u.amplitude / 4095.0f,
669          regs.texturing.proctex_noise_v.amplitude / 4095.0f,
670      };
671      const Common::Vec2f proctex_noise_p = {
672          Pica::f16::FromRaw(regs.texturing.proctex_noise_u.phase).ToFloat32(),
673          Pica::f16::FromRaw(regs.texturing.proctex_noise_v.phase).ToFloat32(),
674      };
675  
676      if (proctex_noise_f != fs_uniform_block_data.data.proctex_noise_f ||
677          proctex_noise_a != fs_uniform_block_data.data.proctex_noise_a ||
678          proctex_noise_p != fs_uniform_block_data.data.proctex_noise_p) {
679          fs_uniform_block_data.data.proctex_noise_f = proctex_noise_f;
680          fs_uniform_block_data.data.proctex_noise_a = proctex_noise_a;
681          fs_uniform_block_data.data.proctex_noise_p = proctex_noise_p;
682          fs_uniform_block_data.dirty = true;
683      }
684  }
685  
686  void RasterizerAccelerated::SyncProcTexBias() {
687      const auto proctex_bias = Pica::f16::FromRaw(regs.texturing.proctex.bias_low |
688                                                   (regs.texturing.proctex_lut.bias_high << 8))
689                                    .ToFloat32();
690      if (proctex_bias != fs_uniform_block_data.data.proctex_bias) {
691          fs_uniform_block_data.data.proctex_bias = proctex_bias;
692          fs_uniform_block_data.dirty = true;
693      }
694  }
695  
696  void RasterizerAccelerated::SyncAlphaTest() {
697      if (regs.framebuffer.output_merger.alpha_test.ref !=
698          static_cast<u32>(fs_uniform_block_data.data.alphatest_ref)) {
699          fs_uniform_block_data.data.alphatest_ref = regs.framebuffer.output_merger.alpha_test.ref;
700          fs_uniform_block_data.dirty = true;
701      }
702  }
703  
704  void RasterizerAccelerated::SyncCombinerColor() {
705      const auto combiner_color = ColorRGBA8(regs.texturing.tev_combiner_buffer_color.raw);
706      if (combiner_color != fs_uniform_block_data.data.tev_combiner_buffer_color) {
707          fs_uniform_block_data.data.tev_combiner_buffer_color = combiner_color;
708          fs_uniform_block_data.dirty = true;
709      }
710  }
711  
712  void RasterizerAccelerated::SyncTevConstColor(
713      const std::size_t stage_index, const Pica::TexturingRegs::TevStageConfig& tev_stage) {
714      const auto const_color = ColorRGBA8(tev_stage.const_color);
715  
716      if (const_color == fs_uniform_block_data.data.const_color[stage_index]) {
717          return;
718      }
719  
720      fs_uniform_block_data.data.const_color[stage_index] = const_color;
721      fs_uniform_block_data.dirty = true;
722  }
723  
724  void RasterizerAccelerated::SyncGlobalAmbient() {
725      const auto color = LightColor(regs.lighting.global_ambient);
726      if (color != fs_uniform_block_data.data.lighting_global_ambient) {
727          fs_uniform_block_data.data.lighting_global_ambient = color;
728          fs_uniform_block_data.dirty = true;
729      }
730  }
731  
732  void RasterizerAccelerated::SyncLightSpecular0(int light_index) {
733      const auto color = LightColor(regs.lighting.light[light_index].specular_0);
734      if (color != fs_uniform_block_data.data.light_src[light_index].specular_0) {
735          fs_uniform_block_data.data.light_src[light_index].specular_0 = color;
736          fs_uniform_block_data.dirty = true;
737      }
738  }
739  
740  void RasterizerAccelerated::SyncLightSpecular1(int light_index) {
741      const auto color = LightColor(regs.lighting.light[light_index].specular_1);
742      if (color != fs_uniform_block_data.data.light_src[light_index].specular_1) {
743          fs_uniform_block_data.data.light_src[light_index].specular_1 = color;
744          fs_uniform_block_data.dirty = true;
745      }
746  }
747  
748  void RasterizerAccelerated::SyncLightDiffuse(int light_index) {
749      const auto color = LightColor(regs.lighting.light[light_index].diffuse);
750      if (color != fs_uniform_block_data.data.light_src[light_index].diffuse) {
751          fs_uniform_block_data.data.light_src[light_index].diffuse = color;
752          fs_uniform_block_data.dirty = true;
753      }
754  }
755  
756  void RasterizerAccelerated::SyncLightAmbient(int light_index) {
757      const auto color = LightColor(regs.lighting.light[light_index].ambient);
758      if (color != fs_uniform_block_data.data.light_src[light_index].ambient) {
759          fs_uniform_block_data.data.light_src[light_index].ambient = color;
760          fs_uniform_block_data.dirty = true;
761      }
762  }
763  
764  void RasterizerAccelerated::SyncLightPosition(int light_index) {
765      const Common::Vec3f position = {
766          Pica::f16::FromRaw(regs.lighting.light[light_index].x).ToFloat32(),
767          Pica::f16::FromRaw(regs.lighting.light[light_index].y).ToFloat32(),
768          Pica::f16::FromRaw(regs.lighting.light[light_index].z).ToFloat32(),
769      };
770  
771      if (position != fs_uniform_block_data.data.light_src[light_index].position) {
772          fs_uniform_block_data.data.light_src[light_index].position = position;
773          fs_uniform_block_data.dirty = true;
774      }
775  }
776  
777  void RasterizerAccelerated::SyncLightSpotDirection(int light_index) {
778      const auto& light = regs.lighting.light[light_index];
779      const auto spot_direction =
780          Common::Vec3f{light.spot_x / 2047.0f, light.spot_y / 2047.0f, light.spot_z / 2047.0f};
781  
782      if (spot_direction != fs_uniform_block_data.data.light_src[light_index].spot_direction) {
783          fs_uniform_block_data.data.light_src[light_index].spot_direction = spot_direction;
784          fs_uniform_block_data.dirty = true;
785      }
786  }
787  
788  void RasterizerAccelerated::SyncLightDistanceAttenuationBias(int light_index) {
789      const f32 dist_atten_bias =
790          Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_bias).ToFloat32();
791  
792      if (dist_atten_bias != fs_uniform_block_data.data.light_src[light_index].dist_atten_bias) {
793          fs_uniform_block_data.data.light_src[light_index].dist_atten_bias = dist_atten_bias;
794          fs_uniform_block_data.dirty = true;
795      }
796  }
797  
798  void RasterizerAccelerated::SyncLightDistanceAttenuationScale(int light_index) {
799      const f32 dist_atten_scale =
800          Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_scale).ToFloat32();
801  
802      if (dist_atten_scale != fs_uniform_block_data.data.light_src[light_index].dist_atten_scale) {
803          fs_uniform_block_data.data.light_src[light_index].dist_atten_scale = dist_atten_scale;
804          fs_uniform_block_data.dirty = true;
805      }
806  }
807  
808  void RasterizerAccelerated::SyncShadowBias() {
809      const auto& shadow = regs.framebuffer.shadow;
810      const f32 constant = Pica::f16::FromRaw(shadow.constant).ToFloat32();
811      const f32 linear = Pica::f16::FromRaw(shadow.linear).ToFloat32();
812  
813      if (constant != fs_uniform_block_data.data.shadow_bias_constant ||
814          linear != fs_uniform_block_data.data.shadow_bias_linear) {
815          fs_uniform_block_data.data.shadow_bias_constant = constant;
816          fs_uniform_block_data.data.shadow_bias_linear = linear;
817          fs_uniform_block_data.dirty = true;
818      }
819  }
820  
821  void RasterizerAccelerated::SyncShadowTextureBias() {
822      const s32 bias = regs.texturing.shadow.bias << 1;
823      if (bias != fs_uniform_block_data.data.shadow_texture_bias) {
824          fs_uniform_block_data.data.shadow_texture_bias = bias;
825          fs_uniform_block_data.dirty = true;
826      }
827  }
828  
829  void RasterizerAccelerated::SyncTextureLodBias(int tex_index) {
830      const auto pica_textures = regs.texturing.GetTextures();
831      const f32 bias = pica_textures[tex_index].config.lod.bias / 256.0f;
832      if (bias != fs_uniform_block_data.data.tex_lod_bias[tex_index]) {
833          fs_uniform_block_data.data.tex_lod_bias[tex_index] = bias;
834          fs_uniform_block_data.dirty = true;
835      }
836  }
837  
838  void RasterizerAccelerated::SyncTextureBorderColor(int tex_index) {
839      const auto pica_textures = regs.texturing.GetTextures();
840      const auto params = pica_textures[tex_index].config;
841      const Common::Vec4f border_color = ColorRGBA8(params.border_color.raw);
842      if (border_color != fs_uniform_block_data.data.tex_border_color[tex_index]) {
843          fs_uniform_block_data.data.tex_border_color[tex_index] = border_color;
844          fs_uniform_block_data.dirty = true;
845      }
846  }
847  
848  void RasterizerAccelerated::SyncClipPlane() {
849      const u32 enable_clip1 = regs.rasterizer.clip_enable != 0;
850      const auto raw_clip_coef = regs.rasterizer.GetClipCoef();
851      const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),
852                                           raw_clip_coef.z.ToFloat32(), raw_clip_coef.w.ToFloat32()};
853      if (enable_clip1 != vs_uniform_block_data.data.enable_clip1 ||
854          new_clip_coef != vs_uniform_block_data.data.clip_coef) {
855          vs_uniform_block_data.data.enable_clip1 = enable_clip1;
856          vs_uniform_block_data.data.clip_coef = new_clip_coef;
857          vs_uniform_block_data.dirty = true;
858      }
859  }
860  
861  } // namespace VideoCore