/ src / video_core / pica / vertex_loader.cpp
vertex_loader.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 "common/logging/log.h"
  7  #include "video_core/pica/vertex_loader.h"
  8  
  9  namespace Pica {
 10  
 11  VertexLoader::VertexLoader(Memory::MemorySystem& memory_, const PipelineRegs& regs)
 12      : memory{memory_} {
 13      const auto& attribute_config = regs.vertex_attributes;
 14      num_total_attributes = attribute_config.GetNumTotalAttributes();
 15  
 16      vertex_attribute_sources.fill(0xdeadbeef);
 17  
 18      for (u32 i = 0; i < 16; i++) {
 19          vertex_attribute_is_default[i] = attribute_config.IsDefaultAttribute(i);
 20      }
 21  
 22      // Setup attribute data from loaders
 23      for (u32 loader = 0; loader < 12; ++loader) {
 24          const auto& loader_config = attribute_config.attribute_loaders[loader];
 25  
 26          u32 offset = 0;
 27  
 28          // TODO: What happens if a loader overwrites a previous one's data?
 29          for (u32 component = 0; component < loader_config.component_count; ++component) {
 30              if (component >= 12) {
 31                  LOG_ERROR(HW_GPU,
 32                            "Overflow in the vertex attribute loader {} trying to load component {}",
 33                            loader, component);
 34                  continue;
 35              }
 36  
 37              u32 attribute_index = loader_config.GetComponent(component);
 38              if (attribute_index < 12) {
 39                  offset = Common::AlignUp(offset,
 40                                           attribute_config.GetElementSizeInBytes(attribute_index));
 41                  vertex_attribute_sources[attribute_index] = loader_config.data_offset + offset;
 42                  vertex_attribute_strides[attribute_index] =
 43                      static_cast<u32>(loader_config.byte_count);
 44                  vertex_attribute_formats[attribute_index] =
 45                      attribute_config.GetFormat(attribute_index);
 46                  vertex_attribute_elements[attribute_index] =
 47                      attribute_config.GetNumElements(attribute_index);
 48                  offset += attribute_config.GetStride(attribute_index);
 49              } else if (attribute_index < 16) {
 50                  // Attribute ids 12, 13, 14 and 15 signify 4, 8, 12 and 16-byte paddings,
 51                  // respectively
 52                  offset = Common::AlignUp(offset, 4);
 53                  offset += (attribute_index - 11) * 4;
 54              } else {
 55                  UNREACHABLE(); // This is truly unreachable due to the number of bits for each
 56                                 // component
 57              }
 58          }
 59      }
 60  }
 61  
 62  VertexLoader::~VertexLoader() = default;
 63  
 64  void VertexLoader::LoadVertex(PAddr base_address, u32 index, u32 vertex, AttributeBuffer& input,
 65                                AttributeBuffer& input_default_attributes) const {
 66      for (s32 i = 0; i < num_total_attributes; ++i) {
 67          // Load the default attribute if we're configured to do so
 68          if (vertex_attribute_is_default[i]) {
 69              input[i] = input_default_attributes[i];
 70              continue;
 71          }
 72  
 73          // TODO(yuriks): In this case, no data gets loaded and the vertex
 74          // remains with the last value it had. This isn't currently maintained
 75          // as global state, however, and so won't work in Citra yet.
 76          if (vertex_attribute_elements[i] == 0) {
 77              LOG_ERROR(HW_GPU, "Vertex retension unimplemented");
 78              continue;
 79          }
 80  
 81          // Load per-vertex data from the loader arrays
 82          const PAddr source_addr =
 83              base_address + vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex;
 84  
 85          switch (vertex_attribute_formats[i]) {
 86          case PipelineRegs::VertexAttributeFormat::BYTE:
 87              LoadAttribute<s8>(source_addr, i, input);
 88              break;
 89          case PipelineRegs::VertexAttributeFormat::UBYTE:
 90              LoadAttribute<u8>(source_addr, i, input);
 91              break;
 92          case PipelineRegs::VertexAttributeFormat::SHORT:
 93              LoadAttribute<s16>(source_addr, i, input);
 94              break;
 95          case PipelineRegs::VertexAttributeFormat::FLOAT:
 96              LoadAttribute<f32>(source_addr, i, input);
 97              break;
 98          }
 99  
100          // Default attribute values set if array elements have < 4 components. This
101          // is *not* carried over from the default attribute settings even if they're
102          // enabled for this attribute.
103          for (u32 comp = vertex_attribute_elements[i]; comp < 4; comp++) {
104              input[i][comp] = comp == 3 ? f24::One() : f24::Zero();
105          }
106      }
107  }
108  
109  } // namespace Pica