summaryrefslogtreecommitdiffstats
path: root/src/video_core/vertex_loader.cpp
blob: 18a7cf1444342b718f6ffc366785e602401b2dd5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <memory>

#include <boost/range/algorithm/fill.hpp>

#include "common/alignment.h"
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/vector_math.h"

#include "core/memory.h"

#include "video_core/debug_utils/debug_utils.h"
#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
#include "video_core/shader/shader.h"
#include "video_core/vertex_loader.h"

namespace Pica {

void VertexLoader::Setup(const Pica::Regs& regs) {
    ASSERT_MSG(!is_setup, "VertexLoader is not intended to be setup more than once.");

    const auto& attribute_config = regs.vertex_attributes;
    num_total_attributes = attribute_config.GetNumTotalAttributes();

    boost::fill(vertex_attribute_sources, 0xdeadbeef);

    for (int i = 0; i < 16; i++) {
        vertex_attribute_is_default[i] = attribute_config.IsDefaultAttribute(i);
    }

    // Setup attribute data from loaders
    for (int loader = 0; loader < 12; ++loader) {
        const auto& loader_config = attribute_config.attribute_loaders[loader];

        u32 offset = 0;

        // TODO: What happens if a loader overwrites a previous one's data?
        for (unsigned component = 0; component < loader_config.component_count; ++component) {
            if (component >= 12) {
                LOG_ERROR(HW_GPU, "Overflow in the vertex attribute loader %u trying to load component %u", loader, component);
                continue;
            }

            u32 attribute_index = loader_config.GetComponent(component);
            if (attribute_index < 12) {
                offset = Common::AlignUp(offset, attribute_config.GetElementSizeInBytes(attribute_index));
                vertex_attribute_sources[attribute_index] = loader_config.data_offset + offset;
                vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
                vertex_attribute_formats[attribute_index] = attribute_config.GetFormat(attribute_index);
                vertex_attribute_elements[attribute_index] = attribute_config.GetNumElements(attribute_index);
                offset += attribute_config.GetStride(attribute_index);
            } else if (attribute_index < 16) {
                // Attribute ids 12, 13, 14 and 15 signify 4, 8, 12 and 16-byte paddings, respectively
                offset = Common::AlignUp(offset, 4);
                offset += (attribute_index - 11) * 4;
            } else {
                UNREACHABLE(); // This is truly unreachable due to the number of bits for each component
            }
        }
    }

    is_setup = true;
}

void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::InputVertex& input, DebugUtils::MemoryAccessTracker& memory_accesses) {
    ASSERT_MSG(is_setup, "A VertexLoader needs to be setup before loading vertices.");

    for (int i = 0; i < num_total_attributes; ++i) {
        if (vertex_attribute_elements[i] != 0) {
            // Load per-vertex data from the loader arrays
            u32 source_addr = base_address + vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex;

            if (g_debug_context && Pica::g_debug_context->recorder) {
                memory_accesses.AddAccess(source_addr, vertex_attribute_elements[i] * (
                    (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::FLOAT) ? 4
                    : (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? 2 : 1));
            }

            switch (vertex_attribute_formats[i]) {
            case Regs::VertexAttributeFormat::BYTE:
            {
                const s8* srcdata = reinterpret_cast<const s8*>(Memory::GetPhysicalPointer(source_addr));
                for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
                    input.attr[i][comp] = float24::FromFloat32(srcdata[comp]);
                }
                break;
            }
            case Regs::VertexAttributeFormat::UBYTE:
            {
                const u8* srcdata = reinterpret_cast<const u8*>(Memory::GetPhysicalPointer(source_addr));
                for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
                    input.attr[i][comp] = float24::FromFloat32(srcdata[comp]);
                }
                break;
            }
            case Regs::VertexAttributeFormat::SHORT:
            {
                const s16* srcdata = reinterpret_cast<const s16*>(Memory::GetPhysicalPointer(source_addr));
                for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
                    input.attr[i][comp] = float24::FromFloat32(srcdata[comp]);
                }
                break;
            }
            case Regs::VertexAttributeFormat::FLOAT:
            {
                const float* srcdata = reinterpret_cast<const float*>(Memory::GetPhysicalPointer(source_addr));
                for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
                    input.attr[i][comp] = float24::FromFloat32(srcdata[comp]);
                }
                break;
            }
            }

            // Default attribute values set if array elements have < 4 components. This
            // is *not* carried over from the default attribute settings even if they're
            // enabled for this attribute.
            for (unsigned int comp = vertex_attribute_elements[i]; comp < 4; ++comp) {
                input.attr[i][comp] = comp == 3 ? float24::FromFloat32(1.0f) : float24::FromFloat32(0.0f);
            }

            LOG_TRACE(HW_GPU, "Loaded %d components of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08x + 0x%04x: %f %f %f %f",
                vertex_attribute_elements[i], i, vertex, index,
                base_address,
                vertex_attribute_sources[i],
                vertex_attribute_strides[i] * vertex,
                input.attr[i][0].ToFloat32(), input.attr[i][1].ToFloat32(), input.attr[i][2].ToFloat32(), input.attr[i][3].ToFloat32());
        } else if (vertex_attribute_is_default[i]) {
            // Load the default attribute if we're configured to do so
            input.attr[i] = g_state.vs.default_attributes[i];
            LOG_TRACE(HW_GPU, "Loaded default attribute %x for vertex %x (index %x): (%f, %f, %f, %f)",
                i, vertex, index,
                input.attr[i][0].ToFloat32(), input.attr[i][1].ToFloat32(),
                input.attr[i][2].ToFloat32(), input.attr[i][3].ToFloat32());
        } else {
            // TODO(yuriks): In this case, no data gets loaded and the vertex
            // remains with the last value it had. This isn't currently maintained
            // as global state, however, and so won't work in Citra yet.
        }
    }
}

}  // namespace Pica