// Copyright 2009 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include "VideoBackends/Software/SWVertexLoader.h" #include #include #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "VideoBackends/Software/DebugUtil.h" #include "VideoBackends/Software/NativeVertexFormat.h" #include "VideoBackends/Software/Rasterizer.h" #include "VideoBackends/Software/SWRenderer.h" #include "VideoBackends/Software/Tev.h" #include "VideoBackends/Software/TransformUnit.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/DataReader.h" #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexLoaderBase.h" #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/XFMemory.h" SWVertexLoader::SWVertexLoader() = default; SWVertexLoader::~SWVertexLoader() = default; void SWVertexLoader::DrawCurrentBatch(u32 base_index, u32 num_indices, u32 base_vertex) { DebugUtil::OnObjectBegin(); u8 primitiveType = 0; switch (m_current_primitive_type) { case PrimitiveType::Points: primitiveType = OpcodeDecoder::GX_DRAW_POINTS; break; case PrimitiveType::Lines: primitiveType = OpcodeDecoder::GX_DRAW_LINES; break; case PrimitiveType::Triangles: primitiveType = OpcodeDecoder::GX_DRAW_TRIANGLES; break; case PrimitiveType::TriangleStrip: primitiveType = OpcodeDecoder::GX_DRAW_TRIANGLE_STRIP; break; } m_setup_unit.Init(primitiveType); // set all states with are stored within video sw for (int i = 0; i < 4; i++) { Rasterizer::SetTevReg(i, Tev::RED_C, PixelShaderManager::constants.kcolors[i][0]); Rasterizer::SetTevReg(i, Tev::GRN_C, PixelShaderManager::constants.kcolors[i][1]); Rasterizer::SetTevReg(i, Tev::BLU_C, PixelShaderManager::constants.kcolors[i][2]); Rasterizer::SetTevReg(i, Tev::ALP_C, PixelShaderManager::constants.kcolors[i][3]); } for (u32 i = 0; i < m_index_generator.GetIndexLen(); i++) { const u16 index = m_cpu_index_buffer[i]; memset(static_cast(&m_vertex), 0, sizeof(m_vertex)); // parse the videocommon format to our own struct format (m_vertex) SetFormat(g_main_cp_state.last_id, primitiveType); ParseVertex(VertexLoaderManager::GetCurrentVertexFormat()->GetVertexDeclaration(), index); // transform this vertex so that it can be used for rasterization (outVertex) OutputVertexData* outVertex = m_setup_unit.GetVertex(); TransformUnit::TransformPosition(&m_vertex, outVertex); outVertex->normal = {}; if (VertexLoaderManager::g_current_components & VB_HAS_NRM0) { TransformUnit::TransformNormal( &m_vertex, (VertexLoaderManager::g_current_components & VB_HAS_NRM2) != 0, outVertex); } TransformUnit::TransformColor(&m_vertex, outVertex); TransformUnit::TransformTexCoord(&m_vertex, outVertex); // assemble and rasterize the primitive m_setup_unit.SetupVertex(); INCSTAT(g_stats.this_frame.num_vertices_loaded) } DebugUtil::OnObjectEnd(); } void SWVertexLoader::SetFormat(u8 attributeIndex, u8 primitiveType) { // matrix index from xf regs or cp memory? if (xfmem.MatrixIndexA.PosNormalMtxIdx != g_main_cp_state.matrix_index_a.PosNormalMtxIdx || xfmem.MatrixIndexA.Tex0MtxIdx != g_main_cp_state.matrix_index_a.Tex0MtxIdx || xfmem.MatrixIndexA.Tex1MtxIdx != g_main_cp_state.matrix_index_a.Tex1MtxIdx || xfmem.MatrixIndexA.Tex2MtxIdx != g_main_cp_state.matrix_index_a.Tex2MtxIdx || xfmem.MatrixIndexA.Tex3MtxIdx != g_main_cp_state.matrix_index_a.Tex3MtxIdx || xfmem.MatrixIndexB.Tex4MtxIdx != g_main_cp_state.matrix_index_b.Tex4MtxIdx || xfmem.MatrixIndexB.Tex5MtxIdx != g_main_cp_state.matrix_index_b.Tex5MtxIdx || xfmem.MatrixIndexB.Tex6MtxIdx != g_main_cp_state.matrix_index_b.Tex6MtxIdx || xfmem.MatrixIndexB.Tex7MtxIdx != g_main_cp_state.matrix_index_b.Tex7MtxIdx) { ERROR_LOG_FMT(VIDEO, "Matrix indices don't match"); } m_vertex.posMtx = xfmem.MatrixIndexA.PosNormalMtxIdx; m_vertex.texMtx[0] = xfmem.MatrixIndexA.Tex0MtxIdx; m_vertex.texMtx[1] = xfmem.MatrixIndexA.Tex1MtxIdx; m_vertex.texMtx[2] = xfmem.MatrixIndexA.Tex2MtxIdx; m_vertex.texMtx[3] = xfmem.MatrixIndexA.Tex3MtxIdx; m_vertex.texMtx[4] = xfmem.MatrixIndexB.Tex4MtxIdx; m_vertex.texMtx[5] = xfmem.MatrixIndexB.Tex5MtxIdx; m_vertex.texMtx[6] = xfmem.MatrixIndexB.Tex6MtxIdx; m_vertex.texMtx[7] = xfmem.MatrixIndexB.Tex7MtxIdx; } template static T ReadNormalized(I value) { T casted = (T)value; if (!std::numeric_limits::is_integer && std::numeric_limits::is_integer) { // normalize if non-float is converted to a float casted *= (T)(1.0 / std::numeric_limits::max()); } return casted; } template static void ReadVertexAttribute(T* dst, DataReader src, const AttributeFormat& format, int base_component, int components, bool reverse) { if (format.enable) { src.Skip(format.offset); src.Skip(base_component * (1 << (format.type >> 1))); int i; for (i = 0; i < std::min(format.components - base_component, components); i++) { int i_dst = reverse ? components - i - 1 : i; switch (format.type) { case VAR_UNSIGNED_BYTE: dst[i_dst] = ReadNormalized(src.Read()); break; case VAR_BYTE: dst[i_dst] = ReadNormalized(src.Read()); break; case VAR_UNSIGNED_SHORT: dst[i_dst] = ReadNormalized(src.Read()); break; case VAR_SHORT: dst[i_dst] = ReadNormalized(src.Read()); break; case VAR_FLOAT: dst[i_dst] = ReadNormalized(src.Read()); break; } ASSERT_MSG(VIDEO, !format.integer || format.type != VAR_FLOAT, "only non-float values are allowed to be streamed as integer"); } for (; i < components; i++) { int i_dst = reverse ? components - i - 1 : i; dst[i_dst] = i == 3; } } } static void ParseColorAttributes(InputVertexData* dst, DataReader& src, const PortableVertexDeclaration& vdec) { const auto set_default_color = [](u8* color, int i) { // The default alpha channel seems to depend on the number of components in the vertex format. const auto& g0 = g_main_cp_state.vtx_attr[g_main_cp_state.last_id].g0; const auto color_elements = i == 0 ? g0.Color0Elements.Value() : g0.Color1Elements.Value(); color[0] = color_elements == ColorComponentCount::RGB ? 255 : 0; color[1] = 255; color[2] = 255; color[3] = 255; }; if (vdec.colors[0].enable) { // Use color0 for channel 0, and color1 for channel 1 if both colors 0 and 1 are present. ReadVertexAttribute(dst->color[0].data(), src, vdec.colors[0], 0, 4, true); if (vdec.colors[1].enable) ReadVertexAttribute(dst->color[1].data(), src, vdec.colors[1], 0, 4, true); else set_default_color(dst->color[1].data(), 1); } else { // If only one of the color attributes is enabled, it is directed to color 0. if (vdec.colors[1].enable) ReadVertexAttribute(dst->color[0].data(), src, vdec.colors[1], 0, 4, true); else set_default_color(dst->color[0].data(), 0); set_default_color(dst->color[1].data(), 1); } } void SWVertexLoader::ParseVertex(const PortableVertexDeclaration& vdec, int index) { DataReader src(m_cpu_vertex_buffer.data(), m_cpu_vertex_buffer.data() + m_cpu_vertex_buffer.size()); src.Skip(index * vdec.stride); ReadVertexAttribute(&m_vertex.position[0], src, vdec.position, 0, 3, false); for (std::size_t i = 0; i < m_vertex.normal.size(); i++) { ReadVertexAttribute(&m_vertex.normal[i][0], src, vdec.normals[i], 0, 3, false); } ParseColorAttributes(&m_vertex, src, vdec); for (std::size_t i = 0; i < m_vertex.texCoords.size(); i++) { ReadVertexAttribute(m_vertex.texCoords[i].data(), src, vdec.texcoords[i], 0, 2, false); // the texmtr is stored as third component of the texCoord if (vdec.texcoords[i].components >= 3) { ReadVertexAttribute(&m_vertex.texMtx[i], src, vdec.texcoords[i], 2, 1, false); } } ReadVertexAttribute(&m_vertex.posMtx, src, vdec.posmtx, 0, 1, false); }