[graphics] shadow volumes (#1247)

* shadow volumes drawing

* fix bugs in clipping case

* add shadow

* fix Q register bugs

* fix bugs
This commit is contained in:
water111
2022-03-26 19:56:54 -04:00
committed by GitHub
parent e38832365c
commit 8d9b0b493b
14 changed files with 2560 additions and 23 deletions
+7
View File
@@ -1007,6 +1007,13 @@ std::string VuDisassembler::to_cpp(const VuInstruction& instr, bool mips2c_forma
case VuInstrK::XTOP:
return fmt::format("vu.{} = xtop();", instr.src.at(0).to_string(m_label_names));
case VuInstrK::OPMULA:
return fmt::format("vu.acc.opmula(vu.{}, vu.{});", instr.src.at(0).to_string(m_label_names),
instr.src.at(1).to_string(m_label_names));
case VuInstrK::OPMSUB:
return fmt::format("vu.acc.opmsub(vu.{}, vu.{}, vu.{});", instr.dst->to_string(m_label_names),
instr.src.at(0).to_string(m_label_names),
instr.src.at(1).to_string(m_label_names));
default:
unk++;
fmt::print("unknown 0 is {}\n", to_string(instr));
+1
View File
@@ -109,6 +109,7 @@ set(RUNTIME_SOURCE
graphics/opengl_renderer/OpenGLRenderer.cpp
graphics/opengl_renderer/Profiler.cpp
graphics/opengl_renderer/Shader.cpp
graphics/opengl_renderer/Shadow_PS2.cpp
graphics/opengl_renderer/ShadowRenderer.cpp
graphics/opengl_renderer/SkyBlendCPU.cpp
graphics/opengl_renderer/SkyBlendGPU.cpp
+61
View File
@@ -1,6 +1,7 @@
#pragma once
#include "immintrin.h"
#include "common/math/Vector.h"
#include <cfloat>
enum class Mask {
NONE = 0,
@@ -76,6 +77,8 @@ struct alignas(16) Vf {
std::string print() const { return fmt::format("{} {} {} {}", x(), y(), z(), w()); }
float length_xyz() const { return std::sqrt(x() * x() + y() * y() + z() * z()); }
std::string print_hex() const {
return fmt::format("0x{:x} 0x{:x} 0x{:x} 0x{:x}", x_as_u32(), y_as_u32(), z_as_u32(),
w_as_u32());
@@ -232,6 +235,14 @@ struct alignas(16) Vf {
}
}
void mini(Mask mask, const Vf& a, const Vf& b) {
for (int i = 0; i < 4; i++) {
if ((u64)mask & (1 << i)) {
data[i] = vu_min(a[i], b[i]);
}
}
}
void fill(float f) {
for (auto& x : data) {
x = f;
@@ -274,6 +285,32 @@ struct alignas(16) Vf {
}
}
u32 add_and_set_sf_s(Mask mask, const Vf& a, float b) {
u32 flag = 0;
for (int i = 0; i < 4; i++) {
if ((u64)mask & (1 << i)) {
data[i] = a[i] + b;
if (data[i] < 0) {
flag = 0x2;
}
}
}
return flag;
}
u32 sub_and_set_sf_s(Mask mask, const Vf& a, float b) {
u32 flag = 0;
for (int i = 0; i < 4; i++) {
if ((u64)mask & (1 << i)) {
data[i] = a[i] - b;
if (data[i] < 0) {
flag = 0x2;
}
}
}
return flag;
}
void sub(Mask mask, const Vf& a, float b) {
for (int i = 0; i < 4; i++) {
if ((u64)mask & (1 << i)) {
@@ -306,6 +343,18 @@ struct alignas(16) Vf {
}
}
void saturate_infs() {
for (auto& val : data) {
if (std::isinf(val)) {
if (val > 0) {
val = FLT_MAX;
} else {
val = -FLT_MAX;
}
}
}
}
void mul(Mask mask, const Vf& a, float b) {
for (int i = 0; i < 4; i++) {
if ((u64)mask & (1 << i)) {
@@ -522,4 +571,16 @@ struct alignas(16) Accumulator {
auto a = _mm_load_ps(_a.data);
_mm_store_ps(data, _mm_mul_ps(a, b));
}
void opmula(const Vf& a, const Vf& b) {
data[0] = a.data[1] * b.data[2];
data[1] = a.data[2] * b.data[0];
data[2] = a.data[0] * b.data[1];
}
void opmsub(Vf& dst, Vf a, Vf b) {
dst.data[0] = data[0] - a.data[1] * b.data[2];
dst.data[1] = data[1] - a.data[2] * b.data[0];
dst.data[2] = data[2] - a.data[0] * b.data[1];
}
};
@@ -682,6 +682,16 @@ void DirectRenderer::handle_ad(const u8* data,
break;
case GsRegisterAddress::FRAME_1:
break;
case GsRegisterAddress::RGBAQ:
// shadow scissor does this?
{
m_prim_building.rgba_reg[0] = data[0];
m_prim_building.rgba_reg[1] = data[1];
m_prim_building.rgba_reg[2] = data[2];
m_prim_building.rgba_reg[3] = data[3];
memcpy(&m_prim_building.Q, data + 4, 4);
}
break;
default:
fmt::print("Address {} is not supported\n", register_address_name(addr));
ASSERT(false);
@@ -391,7 +391,7 @@ void OpenGLRenderer::setup_frame(int window_width_px,
glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth(0.0);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDisable(GL_BLEND);
}
+1
View File
@@ -81,4 +81,5 @@ ShaderLibrary::ShaderLibrary() {
at(ShaderId::OCEAN_TEXTURE) = {"ocean_texture"};
at(ShaderId::OCEAN_TEXTURE_MIPMAP) = {"ocean_texture_mipmap"};
at(ShaderId::OCEAN_COMMON) = {"ocean_common"};
at(ShaderId::SHADOW) = {"shadow"};
}
+1 -1
View File
@@ -38,6 +38,7 @@ enum class ShaderId {
OCEAN_TEXTURE = 13,
OCEAN_TEXTURE_MIPMAP = 14,
OCEAN_COMMON = 15,
SHADOW = 16,
MAX_SHADERS
};
@@ -45,7 +46,6 @@ class ShaderLibrary {
public:
ShaderLibrary();
Shader& operator[](ShaderId id) { return m_shaders[(int)id]; }
Shader& at(ShaderId id) { return m_shaders[(int)id]; }
private:
@@ -1,16 +1,426 @@
#include "ShadowRenderer.h"
#include "third-party/imgui/imgui.h"
#include <cfloat>
ShadowRenderer::ShadowRenderer(const std::string& name, BucketId my_id)
: BucketRenderer(name, my_id), m_direct(name, my_id, 0x4000) {}
: BucketRenderer(name, my_id) {
// create OpenGL objects
glGenBuffers(1, &m_ogl.vertex_buffer);
glGenBuffers(2, m_ogl.index_buffer);
glGenVertexArrays(1, &m_ogl.vao);
// set up the vertex array
glBindVertexArray(m_ogl.vao);
for (int i = 0; i < 2; i++) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_INDICES * sizeof(u32), nullptr, GL_STREAM_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(Vertex), nullptr, GL_STREAM_DRAW);
// xyz
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, // location 0 in the shader
3, // 3 floats per vert
GL_FLOAT, // floats
GL_TRUE, // normalized, ignored,
sizeof(Vertex), //
(void*)offsetof(Vertex, xyz) // offset in array
);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void ShadowRenderer::draw_debug_window() {
m_direct.draw_debug_window();
ImGui::Checkbox("Volume", &m_debug_draw_volume);
ImGui::Text("Vert: %d, Front: %d, Back: %d\n", m_next_vertex, m_next_front_index,
m_next_back_index);
}
ShadowRenderer::~ShadowRenderer() {
glDeleteBuffers(1, &m_ogl.vertex_buffer);
glDeleteBuffers(2, m_ogl.index_buffer);
glDeleteVertexArrays(1, &m_ogl.vao);
}
void ShadowRenderer::xgkick(u16 imm) {
u32 ind_of_fan_start = UINT32_MAX;
bool fan_running = false;
const u8* data = (const u8*)(m_vu_data + imm);
u8 rgba[4] = {1, 2, 3, 4};
bool eop = false;
u32 offset = 0;
while (!eop) {
GifTag tag(data + offset);
offset += 16;
// unpack registers.
// faster to do it once outside of the nloop loop.
GifTag::RegisterDescriptor reg_desc[16];
u32 nreg = tag.nreg();
for (u32 i = 0; i < nreg; i++) {
reg_desc[i] = tag.reg(i);
}
auto format = tag.flg();
if (format == GifTag::Format::PACKED) {
if (tag.pre()) {
GsPrim prim(tag.prim());
ASSERT(prim.kind() == GsPrim::Kind::TRI_FAN);
}
for (u32 loop = 0; loop < tag.nloop(); loop++) {
for (u32 reg = 0; reg < nreg; reg++) {
switch (reg_desc[reg]) {
case GifTag::RegisterDescriptor::AD: {
u64 value;
GsRegisterAddress addr;
memcpy(&value, data + offset, sizeof(u64));
memcpy(&addr, data + offset + 8, sizeof(GsRegisterAddress));
switch (addr) {
case GsRegisterAddress::TEXFLUSH:
break;
case GsRegisterAddress::RGBAQ: {
rgba[0] = data[0 + offset];
rgba[1] = data[1 + offset];
rgba[2] = data[2 + offset];
rgba[3] = data[3 + offset];
float Q;
memcpy(&Q, data + offset + 4, 4);
// fmt::print("rgba: {} {} {} {}: {}\n", rgba[0], rgba[1], rgba[2], rgba[3], Q);
} break;
default:
fmt::print("Address {} is not supported\n", register_address_name(addr));
ASSERT(false);
}
} break;
case GifTag::RegisterDescriptor::ST: {
float s, t;
memcpy(&s, data + offset, 4);
memcpy(&t, data + offset + 4, 4);
// fmt::print("st: {} {}\n", s, t);
} break;
case GifTag::RegisterDescriptor::RGBAQ:
for (int i = 0; i < 4; i++) {
rgba[i] = data[offset + i * 4];
}
// fmt::print("rgbap: {} {} {} {}\n", rgba[0], rgba[1], rgba[2], rgba[3]);
break;
case GifTag::RegisterDescriptor::XYZF2:
// handle_xyzf2_packed(data + offset, render_state, prof);
{
u32 x, y;
memcpy(&x, data + offset, 4);
memcpy(&y, data + offset + 4, 4);
u64 upper;
memcpy(&upper, data + offset + 8, 8);
u32 z = (upper >> 4) & 0xffffff;
x <<= 16;
y <<= 16;
z <<= 8;
u32 vidx = m_next_vertex++;
auto& v = m_vertices[vidx];
ASSERT(m_next_vertex < MAX_VERTICES);
v.xyz[0] = (float)x / (float)UINT32_MAX;
v.xyz[1] = (float)y / (float)UINT32_MAX;
v.xyz[2] = (float)z / (float)UINT32_MAX;
if (ind_of_fan_start == UINT32_MAX) {
ind_of_fan_start = vidx;
} else {
if (fan_running) {
// todo, actually use triangle fans in opengl...
if (rgba[0] > 0) {
// back
m_back_indices[m_next_back_index++] = vidx;
m_back_indices[m_next_back_index++] = vidx - 1;
m_back_indices[m_next_back_index++] = ind_of_fan_start;
} else {
m_front_indices[m_next_front_index++] = vidx;
m_front_indices[m_next_front_index++] = vidx - 1;
m_front_indices[m_next_front_index++] = ind_of_fan_start;
}
} else {
fan_running = true;
}
}
// fmt::print("xyzfadc: {} {} {} {} {}\n", x, y, z, f, adc);
}
break;
default:
fmt::print("Register {} is not supported in packed mode yet\n",
reg_descriptor_name(reg_desc[reg]));
ASSERT(false);
}
offset += 16; // PACKED = quadwords
}
}
} else {
ASSERT(false); // format not packed or reglist.
}
eop = tag.eop();
}
}
void ShadowRenderer::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& /*prof*/) {
while (dma.current_tag_offset() != render_state->next_bucket) {
ScopedProfilerNode& prof) {
if (!m_enabled) {
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
}
return;
}
m_next_vertex = 0;
m_next_back_index = 0;
m_next_front_index = 0;
// jump to bucket
auto data0 = dma.read_and_advance();
ASSERT(data0.vif1() == 0);
ASSERT(data0.vif0() == 0);
ASSERT(data0.size_bytes == 0);
// see if bucket is empty or not
if (dma.current_tag().kind == DmaTag::Kind::CALL) {
// renderer didn't run, let's just get out of here.
for (int i = 0; i < 4; i++) {
dma.read_and_advance();
}
ASSERT(dma.current_tag_offset() == render_state->next_bucket);
return;
}
{
// constants
auto constants = dma.read_and_advance();
auto v0 = constants.vifcode0();
auto v1 = constants.vifcode1();
ASSERT(v0.kind == VifCode::Kind::STCYCL);
ASSERT(v0.immediate == 0x404);
ASSERT(v1.kind == VifCode::Kind::UNPACK_V4_32);
ASSERT(v1.immediate == Vu1Data::CONSTANTS);
ASSERT(v1.num == 13);
memcpy(m_vu_data + v1.immediate, constants.data, v1.num * 16);
}
{
// gif constants
auto constants = dma.read_and_advance();
auto v0 = constants.vifcode0();
auto v1 = constants.vifcode1();
ASSERT(v0.kind == VifCode::Kind::STCYCL);
ASSERT(v0.immediate == 0x404);
ASSERT(v1.kind == VifCode::Kind::UNPACK_V4_32);
ASSERT(v1.immediate == Vu1Data::GIF_CONSTANTS);
ASSERT(v1.num == 4);
memcpy(m_vu_data + v1.immediate, constants.data, v1.num * 16);
}
{
// matrix constants
auto constants = dma.read_and_advance();
auto v0 = constants.vifcode0();
auto v1 = constants.vifcode1();
ASSERT(v0.kind == VifCode::Kind::STCYCL);
ASSERT(v0.immediate == 0x404);
ASSERT(v1.kind == VifCode::Kind::UNPACK_V4_32);
ASSERT(v1.immediate == Vu1Data::MATRIX);
ASSERT(v1.num == 4);
memcpy(m_vu_data + v1.immediate, constants.data, v1.num * 16);
}
{
// exec 10
auto mscal = dma.read_and_advance();
ASSERT(mscal.vifcode1().kind == VifCode::Kind::FLUSHE);
ASSERT(mscal.vifcode0().kind == VifCode::Kind::MSCALF);
ASSERT(mscal.vifcode0().immediate == Vu1Code::INIT);
run_mscal10_vu2c();
}
{
// init gs direct
dma.read_and_advance();
}
while (dma.current_tag().kind != DmaTag::Kind::CALL) {
auto next = dma.read_and_advance();
auto v1 = next.vifcode1();
if (next.vifcode0().kind == VifCode::Kind::FLUSHA &&
next.vifcode1().kind == VifCode::Kind::UNPACK_V4_32) {
auto up = next.vifcode1();
VifCodeUnpack unpack(up);
ASSERT(!unpack.use_tops_flag);
ASSERT((u32)unpack.addr_qw + up.num < 1024);
memcpy(m_vu_data + unpack.addr_qw, next.data, up.num * 16);
ASSERT(up.num * 16 == next.size_bytes);
} else if (next.vifcode0().kind == VifCode::Kind::NOP &&
next.vifcode1().kind == VifCode::Kind::UNPACK_V4_32) {
auto up = next.vifcode1();
VifCodeUnpack unpack(up);
ASSERT(!unpack.use_tops_flag);
ASSERT((u32)unpack.addr_qw + up.num < 1024);
memcpy(m_vu_data + unpack.addr_qw, next.data, up.num * 16);
ASSERT(up.num * 16 == next.size_bytes);
} else if (next.vifcode0().kind == VifCode::Kind::NOP &&
next.vifcode1().kind == VifCode::Kind::UNPACK_V4_8) {
auto up = VifCodeUnpack(v1);
ASSERT(!up.use_tops_flag);
ASSERT(up.is_unsigned);
u16 addr = up.addr_qw;
ASSERT(addr + v1.num <= 1024);
u32 temp[4];
for (u32 i = 0; i < v1.num; i++) {
for (u32 j = 0; j < 4; j++) {
temp[j] = next.data[4 * i + j];
}
memcpy(m_vu_data + addr + i, temp, 16);
}
u32 offset = 4 * v1.num;
ASSERT(offset + 16 == next.size_bytes);
u32 after[4];
memcpy(&after, next.data + offset, 16);
ASSERT(after[0] == 0);
ASSERT(after[1] == 0);
ASSERT(after[2] == 0);
VifCode mscal(after[3]);
ASSERT(mscal.kind == VifCode::Kind::MSCALF);
run_mscal_vu2c(mscal.immediate);
} else if (next.vifcode0().kind == VifCode::Kind::FLUSHA &&
next.vifcode1().kind == VifCode::Kind::DIRECT) {
dma.read_and_advance();
dma.read_and_advance();
dma.read_and_advance();
} else {
fmt::print("{} {}\n", next.vifcode0().print(), next.vifcode1().print());
ASSERT(false);
}
}
for (int i = 0; i < 4; i++) {
dma.read_and_advance();
}
ASSERT(dma.current_tag_offset() == render_state->next_bucket);
draw(render_state, prof);
}
void ShadowRenderer::draw(SharedRenderState* render_state, ScopedProfilerNode& prof) {
// enable stencil!
glEnable(GL_STENCIL_TEST);
glStencilMask(0xFF);
u32 clear_vertices = m_next_vertex;
m_vertices[m_next_vertex++] = Vertex{math::Vector3f(0.3, 0.3, 0), 0};
m_vertices[m_next_vertex++] = Vertex{math::Vector3f(0.3, 0.7, 0), 0};
m_vertices[m_next_vertex++] = Vertex{math::Vector3f(0.7, 0.3, 0), 0};
m_vertices[m_next_vertex++] = Vertex{math::Vector3f(0.7, 0.7, 0), 0};
m_front_indices[m_next_front_index++] = clear_vertices;
m_front_indices[m_next_front_index++] = clear_vertices + 1;
m_front_indices[m_next_front_index++] = clear_vertices + 2;
m_front_indices[m_next_front_index++] = clear_vertices + 3;
m_front_indices[m_next_front_index++] = clear_vertices + 2;
m_front_indices[m_next_front_index++] = clear_vertices + 1;
glBindVertexArray(m_ogl.vao);
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, m_next_vertex * sizeof(Vertex), m_vertices, GL_STREAM_DRAW);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glDepthFunc(GL_GEQUAL);
render_state->shaders.at(ShaderId::SHADOW).activate();
glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::SHADOW].id(), "color_uniform"),
0., 0.4, 0., 0.5);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_next_back_index * sizeof(u32), m_back_indices,
GL_STREAM_DRAW);
// First pass.
// here, we don't write depth or color.
// but we increment stencil on depth fail.
glDepthMask(GL_FALSE); // no depth writes.
if (m_debug_draw_volume) {
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
} else {
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no color writes.
}
glStencilFunc(GL_ALWAYS, 0, 0); // always pass stencil
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); // increment on depth fail.
glDrawElements(GL_TRIANGLES, m_next_back_index, GL_UNSIGNED_INT, nullptr);
if (m_debug_draw_volume) {
glDisable(GL_BLEND);
glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::SHADOW].id(), "color_uniform"),
0., 0.0, 0., 0.5);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, m_next_back_index, GL_UNSIGNED_INT, nullptr);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_BLEND);
}
prof.add_draw_call();
prof.add_tri(m_next_back_index / 3);
glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::SHADOW].id(), "color_uniform"),
0.4, 0.0, 0., 0.5);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer[0]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_next_front_index * sizeof(u32), m_front_indices,
GL_STREAM_DRAW);
// Second pass.
// same settings, but decrement.
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); // decrement on depth fail.
glDrawElements(GL_TRIANGLES, (m_next_front_index - 6), GL_UNSIGNED_INT, nullptr);
if (m_debug_draw_volume) {
glDisable(GL_BLEND);
glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::SHADOW].id(), "color_uniform"),
0., 0.0, 0., 0.5);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, (m_next_front_index - 6), GL_UNSIGNED_INT, nullptr);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_BLEND);
}
prof.add_draw_call();
prof.add_tri(m_next_front_index / 3);
// finally, draw shadow.
glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::SHADOW].id(), "color_uniform"),
0.13, 0.13, 0.13, 0.5);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// glStencilFunc(GL_GREATER, 0, 0);
glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glDepthFunc(GL_ALWAYS);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ZERO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)(sizeof(u32) * (m_next_front_index - 6)));
prof.add_draw_call();
prof.add_tri(2);
glBlendEquation(GL_FUNC_ADD);
glDepthMask(GL_TRUE);
glDisable(GL_STENCIL_TEST);
}
+117 -2
View File
@@ -1,14 +1,129 @@
#pragma once
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/opengl_renderer/DirectRenderer.h"
#include "game/common/vu.h"
class ShadowRenderer : public BucketRenderer {
public:
ShadowRenderer(const std::string& name, BucketId my_id);
~ShadowRenderer();
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
private:
DirectRenderer m_direct;
void run_mscal_vu2c(u16 imm);
void xgkick(u16 imm);
void run_mscal10_vu2c();
void handle_jalr_to_end_block(u16 val, u32& first_flag, u32& second_flag);
void handle_bal52();
void draw(SharedRenderState* render_state, ScopedProfilerNode& prof);
Vf m_vu_data[1024];
enum Vu1Data {
MATRIX = 0,
CONSTANTS = 0x370,
GIF_CONSTANTS = 0x3ac,
};
enum Vu1Code {
INIT = 10,
};
void lq_buffer(Mask mask, Vf& dest, u16 addr) {
ASSERT(addr < 1024);
for (int i = 0; i < 4; i++) {
if ((u64)mask & (1 << i)) {
dest[i] = m_vu_data[addr].data[i];
}
}
}
void sq_buffer(Mask mask, const Vf& val, u16 addr) {
ASSERT(addr < 1024);
for (int i = 0; i < 4; i++) {
if ((u64)mask & (1 << i)) {
m_vu_data[addr].data[i] = val[i];
}
}
}
void ilw_buffer(Mask mask, u16& dest, u16 addr) {
ASSERT(addr < 1024);
switch (mask) {
case Mask::x:
dest = m_vu_data[addr].x_as_u16();
break;
case Mask::y:
dest = m_vu_data[addr].y_as_u16();
break;
case Mask::z:
dest = m_vu_data[addr].z_as_u16();
break;
case Mask::w:
dest = m_vu_data[addr].w_as_u16();
break;
default:
ASSERT(false);
}
}
void isw_buffer(Mask mask, u16 src, u16 addr) {
ASSERT(addr < 1024);
u32 val32 = src;
switch (mask) {
case Mask::x:
memcpy(&m_vu_data[addr].data[0], &val32, 4);
break;
case Mask::y:
memcpy(&m_vu_data[addr].data[1], &val32, 4);
break;
case Mask::z:
memcpy(&m_vu_data[addr].data[2], &val32, 4);
break;
case Mask::w:
memcpy(&m_vu_data[addr].data[3], &val32, 4);
break;
default:
ASSERT(false);
}
}
struct Vu {
const Vf vf00;
Vu() : vf00(0, 0, 0, 1) {}
u16 vi01, vi02, vi03, vi04, vi05, vi06, vi07, vi08, vi09, vi10, vi12, vi15;
Vf vf01, vf02, vf03, vf04, vf05, vf06, vf07, vf08, vf09, vf10, vf11, vf12, vf13, vf14, vf15,
vf16, vf17, vf18, vf19, vf20, vf21, vf22, vf23, vf24, vf25, vf26, vf27, vf28, vf29, vf30,
vf31;
Accumulator acc;
float Q;
const u16 vi00 = 0;
} vu;
struct Vertex {
math::Vector3f xyz;
u32 flag; // 1 if front face, 0 otherwise
};
static_assert(sizeof(Vertex) == 16);
static constexpr int MAX_VERTICES = 8192;
static constexpr int MAX_INDICES = 8192;
Vertex m_vertices[MAX_VERTICES];
u32 m_front_indices[MAX_INDICES];
u32 m_back_indices[MAX_INDICES];
u32 m_next_vertex = 0;
u32 m_next_front_index = 0;
u32 m_next_back_index = 0;
struct {
// index is front, back
GLuint vertex_buffer, index_buffer[2], vao;
} m_ogl;
bool m_debug_draw_volume = false;
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,8 @@
#version 430 core
out vec4 color;
uniform vec4 color_uniform;
void main() {
color = color_uniform;
}
@@ -0,0 +1,10 @@
#version 430 core
layout (location = 0) in vec3 position_in;
void main() {
// Note: position.y is multiplied by 32 instead of 16 to undo the half-height for interlacing stuff.
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z * 2 - 1., 1.0);
// scissoring area adjust
gl_Position.y *= 512.0/448.0;
}
+32 -15
View File
@@ -64,7 +64,7 @@ void exec_0(ExecutionContext* c) {
}
void exec_28(ExecutionContext* c) {
// nop | mul.xyzw vf27, vf20, Q 28
// nop | mul.xyzw vf27, vf20, Q 28
c->vfs[vf27].vf.mul(Mask::xyzw, c->vf_src(vf20).vf, c->Q);
// div Q, vf13.x, vf17.x | sub.xyzw vf19, vf01, vf03 29
c->vfs[vf19].vf.sub(Mask::xyzw, c->vf_src(vf01).vf, c->vf_src(vf03).vf); c->Q = c->vfs[vf13].vf.x() / c->vfs[vf17].vf.x();
@@ -81,9 +81,10 @@ void exec_28(ExecutionContext* c) {
// nop | mul.xyz vf15, vf19, vf02 35
c->vfs[vf15].vf.mul(Mask::xyz, c->vf_src(vf19).vf, c->vf_src(vf02).vf);
// div Q, vf14.x, vf18.x | mul.xyzw vf12, vf04, vf02 36
float oldQ = c->Q;
c->vfs[vf12].vf.mul(Mask::xyzw, c->vf_src(vf04).vf, c->vf_src(vf02).vf); c->Q = c->vfs[vf14].vf.x() / c->vfs[vf18].vf.x();
// move.xyzw vf07, vf03 | mul.xyzw vf28, vf28, Q 37
c->vfs[vf28].vf.mul(Mask::xyzw, c->vf_src(vf28).vf, c->Q); c->vfs[vf07].vf.move(Mask::xyzw, c->vf_src(vf03).vf);
c->vfs[vf28].vf.mul(Mask::xyzw, c->vf_src(vf28).vf, oldQ); c->vfs[vf07].vf.move(Mask::xyzw, c->vf_src(vf03).vf);
// move.xyzw vf08, vf04 | mul.xyz vf16, vf20, vf02 38
c->vfs[vf16].vf.mul(Mask::xyz, c->vf_src(vf20).vf, c->vf_src(vf02).vf); c->vfs[vf08].vf.move(Mask::xyzw, c->vf_src(vf04).vf);
// move.xyzw vf09, vf05 | addy.x vf11, vf11, vf11 39
@@ -111,37 +112,51 @@ void exec_28(ExecutionContext* c) {
// nop | addw.x vf11, vf11, vf11 50
c->vfs[vf11].vf.add(Mask::x, c->vf_src(vf11).vf, c->vf_src(vf11).vf.w());
// nop | mul.xyzw vf13, vf09, vf02 51
c->vfs[vf13].vf.mul(Mask::xyzw, c->vf_src(vf09).vf, c->vf_src(vf02).vf);
c->vfs[vf13].vf.saturate_infs();
// nop | addw.x vf12, vf12, vf12 52
c->vfs[vf12].vf.add(Mask::x, c->vf_src(vf12).vf, c->vf_src(vf12).vf.w());
// nop | mul.xyz vf17, vf21, vf02 53
c->vfs[vf17].vf.mul(Mask::xyz, c->vf_src(vf21).vf, c->vf_src(vf02).vf);
// nop | mul.xyzw vf14, vf10, vf02 54
c->vfs[vf14].vf.mul(Mask::xyzw, c->vf_src(vf10).vf, c->vf_src(vf02).vf);
c->vfs[vf14].vf.saturate_infs();
// div Q, vf11.x, vf15.x | mul.xyz vf18, vf22, vf02 55
c->vfs[vf18].vf.mul(Mask::xyz, c->vf_src(vf22).vf, c->vf_src(vf02).vf); c->Q = c->vfs[vf11].vf.x() / c->vfs[vf15].vf.x();
// nop | addy.x vf13, vf13, vf13 56
c->vfs[vf13].vf.add(Mask::x, c->vf_src(vf13).vf, c->vf_src(vf13).vf.y());
// nop | addy.x vf17, vf17, vf17 57
c->vfs[vf17].vf.add(Mask::x, c->vf_src(vf17).vf, c->vf_src(vf17).vf.y());
// nop | addy.x vf14, vf14, vf14 58
c->vfs[vf14].vf.add(Mask::x, c->vf_src(vf14).vf, c->vf_src(vf14).vf.y());
// nop | addy.x vf18, vf18, vf18 59
c->vfs[vf18].vf.add(Mask::x, c->vf_src(vf18).vf, c->vf_src(vf18).vf.y());
// nop | addz.x vf13, vf13, vf13 60
c->vfs[vf13].vf.add(Mask::x, c->vf_src(vf13).vf, c->vf_src(vf13).vf.z());
// nop | addz.x vf17, vf17, vf17 61
c->vfs[vf17].vf.add(Mask::x, c->vf_src(vf17).vf, c->vf_src(vf17).vf.z());
// div Q, vf12.x, vf16.x | addz.x vf14, vf14, vf14 62
oldQ = c->Q;
c->vfs[vf14].vf.add(Mask::x, c->vf_src(vf14).vf, c->vf_src(vf14).vf.z()); c->Q = c->vfs[vf12].vf.x() / c->vfs[vf16].vf.x();
// nop | mul.xyzw vf19, vf19, Q 63
c->vfs[vf19].vf.mul(Mask::xyzw, c->vf_src(vf19).vf, c->Q);
c->vfs[vf19].vf.mul(Mask::xyzw, c->vf_src(vf19).vf, oldQ);
// move.xyzw vf28, vf21 | addz.x vf18, vf18, vf18 64
c->vfs[vf18].vf.add(Mask::x, c->vf_src(vf18).vf, c->vf_src(vf18).vf.z()); c->vfs[vf28].vf.move(Mask::xyzw, c->vf_src(vf21).vf);
// move.xyzw vf29, vf22 | addw.x vf13, vf13, vf13 65
c->vfs[vf13].vf.add(Mask::x, c->vf_src(vf13).vf, c->vf_src(vf13).vf.w()); c->vfs[vf29].vf.move(Mask::xyzw, c->vf_src(vf22).vf);
c->vfs[vf13].vf.saturate_infs();
// nop | addw.x vf14, vf14, vf14 :e 66
c->vfs[vf14].vf.add(Mask::x, c->vf_src(vf14).vf, c->vf_src(vf14).vf.w());
c->vfs[vf14].vf.saturate_infs();
// nop | sub.xyzw vf07, vf07, vf19 67
c->vfs[vf07].vf.sub(Mask::xyzw, c->vf_src(vf07).vf, c->vf_src(vf19).vf);
}
@@ -164,9 +179,10 @@ void exec_68(ExecutionContext* c) {
// nop | nop 75
// div Q, vf14.x, vf18.x | nop 76
float oldQ = c->Q;
c->Q = c->vfs[vf14].vf.x() / c->vfs[vf18].vf.x();
// nop | mul.xyzw vf28, vf28, Q 77
c->vfs[vf28].vf.mul(Mask::xyzw, c->vf_src(vf28).vf, c->Q);
c->vfs[vf28].vf.mul(Mask::xyzw, c->vf_src(vf28).vf, oldQ);
// nop | nop 78
// nop | nop 79
@@ -174,7 +190,7 @@ void exec_68(ExecutionContext* c) {
// nop | nop 80
// nop | sub.xyzw vf25, vf25, vf28 81
c->vfs[vf25].vf.sub(Mask::xyzw, c->vf_src(vf25).vf, c->vf_src(vf28).vf);
c->vfs[vf25].vf.sub(Mask::xyzw, c->vf_src(vf25).vf, c->vf_src(vf28).vf); // was bad
// nop | nop 82
// nop | mul.xyzw vf29, vf29, Q 83
@@ -324,6 +340,7 @@ u64 execute(void* ctxt) {
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
c->sqc2(vf9, 0, a2); // sqc2 vf9, 0(a2)
c->daddiu(a2, a2, 16); // daddiu a2, a2, 16
// nop // sll r0, r0, 0
bc = c->sgpr64(a0) != 0; // bne a0, r0, L101
@@ -398,6 +415,7 @@ u64 execute(void* ctxt) {
// nop // sll r0, r0, 0
c->mov128_vf_gpr(vf6, t2); // qmtc2.ni vf6, t2
exec_28(c);// Unknown instr: vcallms 28
// nop // sll r0, r0, 0
c->daddiu(a0, a0, -4); // daddiu a0, a0, -4
c->lq(a3, 0, a2); // lq a3, 0(a2)
@@ -430,7 +448,7 @@ u64 execute(void* ctxt) {
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
c->mov128_gpr_vf(a3, vf23); // qmfc2.i a3, vf23
c->mov128_gpr_vf(a3, vf23); // qmfc2.i a3, vf23 // 39
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
@@ -438,11 +456,11 @@ u64 execute(void* ctxt) {
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
c->sq(a3, 0, v1); // sq a3, 0(v1)
c->sq(a3, 0, v1); // sq a3, 0(v1) // 47
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
c->mov128_gpr_vf(a3, vf24); // qmfc2.ni a3, vf24
c->mov128_gpr_vf(a3, vf24); // qmfc2.ni a3, vf24 // 51
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
@@ -450,7 +468,7 @@ u64 execute(void* ctxt) {
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
c->sq(a3, 16, v1); // sq a3, 16(v1)
c->sq(a3, 16, v1); // sq a3, 16(v1) // 59
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
@@ -458,7 +476,7 @@ u64 execute(void* ctxt) {
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
c->mov128_gpr_vf(a3, vf25); // qmfc2.ni a3, vf25
c->mov128_gpr_vf(a3, vf25); // qmfc2.ni a3, vf25 // 67
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
@@ -466,7 +484,7 @@ u64 execute(void* ctxt) {
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
c->sq(a3, 32, v1); // sq a3, 32(v1)
c->sq(a3, 32, v1); // sq a3, 32(v1) // 75
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
@@ -474,7 +492,7 @@ u64 execute(void* ctxt) {
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
c->mov128_gpr_vf(a3, vf26); // qmfc2.ni a3, vf26
c->mov128_gpr_vf(a3, vf26); // qmfc2.ni a3, vf26 // 83
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
@@ -482,7 +500,7 @@ u64 execute(void* ctxt) {
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
c->sq(a3, 48, v1); // sq a3, 48(v1)
c->sq(a3, 48, v1); // sq a3, 48(v1) // 91
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
@@ -505,7 +523,6 @@ u64 execute(void* ctxt) {
c->mov128_vf_gpr(vf6, t2); // qmtc2.ni vf6, t2
if (bc) {goto block_3;} // branch non-likely
block_4:
exec_68(c);// Unknown instr: vcallms 68
// nop // sll r0, r0, 0
@@ -637,7 +654,7 @@ u64 execute(void* ctxt) {
// nop // vnop
c->vadd(DEST::xyzw, vf1, vf1, vf4); // vadd.xyzw vf1, vf1, vf4
//beq r0, r0, L91 // beq r0, r0, L91
c->sqc2(vf1, 0, a0); // sqc2 vf1, 0(a0)
c->sqc2(vf1, 0, a0); // sqc2 vf1, 0(a0) was bad
goto block_7; // branch always
+1
View File
@@ -2,6 +2,7 @@
#include <cstring>
#include <cmath>
#include <cfloat>
#include "common/common_types.h"
#include "game/mips2c/mips2c_table.h"