tfrag improvements (#970)

* tfrag improvements

* cleanup

* one last fix

* clang
This commit is contained in:
water111 2021-11-15 20:07:10 -05:00 committed by GitHub
parent dcbd90a3f3
commit 7292cb5765
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1396 additions and 50 deletions

View file

@ -44,7 +44,7 @@ void diff_dma_chains(DmaFollower ref, DmaFollower dma) {
auto ref_result = ref.read_and_advance();
auto dma_result = dma.read_and_advance();
for (int i = 0; i < ref_result.size_bytes; i++) {
for (int i = 0; i < (int)ref_result.size_bytes; i++) {
if (ref_result.data[i] != dma_result.data[i]) {
fmt::print("Bad data ({} vs {}) at {} into transfer: {} {}\n", ref_result.data[i],
dma_result.data[i], i, ref_tag.print(), dma_tag.print());

View file

@ -95,6 +95,7 @@ set(RUNTIME_SOURCE
graphics/opengl_renderer/SkyRenderer.cpp
graphics/opengl_renderer/SpriteRenderer.cpp
graphics/opengl_renderer/TextureUploadHandler.cpp
graphics/opengl_renderer/tfrag/BufferedRenderer.cpp
graphics/opengl_renderer/tfrag/program6_cpu.cpp
graphics/opengl_renderer/tfrag/tfrag_unpack.cpp
graphics/opengl_renderer/tfrag/TFragment.cpp

View file

@ -71,6 +71,8 @@ void DirectRenderer::draw_debug_window() {
ImGui::Checkbox("red", &m_debug_state.red);
ImGui::SameLine();
ImGui::Checkbox("always", &m_debug_state.always_draw);
ImGui::SameLine();
ImGui::Checkbox("no mip", &m_debug_state.disable_mipmap);
if (m_mode == Mode::SPRITE_CPU) {
ImGui::Checkbox("draw1", &m_sprite_mode.do_first_draw);
@ -128,6 +130,7 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state, ScopedProfil
m_test_state_needs_gl_update = false;
}
// I think it's important that this comes last.
if (m_texture_state.needs_gl_update) {
update_gl_texture(render_state);
m_texture_state.needs_gl_update = false;
@ -218,7 +221,10 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state, ScopedProfil
render_state->shaders[ShaderId::SPRITE_CPU_AFAIL].activate();
glDepthMask(GL_FALSE);
glDrawArrays(GL_TRIANGLES, 0, m_prim_buffer.vert_count);
if (m_test_state.depth_writes) {
glDepthMask(GL_TRUE);
}
m_prim_gl_state_needs_gl_update = true;
m_blend_state_needs_gl_update = true;
draw_count++;
@ -349,7 +355,8 @@ void DirectRenderer::update_gl_texture(SharedRenderState* render_state) {
}
if (m_texture_state.enable_tex_filt) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
m_debug_state.disable_mipmap ? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@ -391,8 +398,9 @@ void DirectRenderer::update_gl_blend() {
void DirectRenderer::update_gl_test() {
const auto& state = m_test_state;
glEnable(GL_DEPTH_TEST);
if (state.zte) {
glEnable(GL_DEPTH_TEST);
switch (state.ztst) {
case GsTest::ZTest::NEVER:
glDepthFunc(GL_NEVER);
@ -769,13 +777,14 @@ void DirectRenderer::handle_zbuf1(u64 val,
assert(x.psm() == TextureFormat::PSMZ24);
assert(x.zbp() == 448);
bool write = x.zmsk();
bool write = !x.zmsk();
// assert(write);
if (write != m_test_state.depth_writes) {
m_stats.flush_from_zbuf++;
flush_pending(render_state, prof);
m_test_state_needs_gl_update = true;
m_prim_gl_state_needs_gl_update = true;
m_test_state.depth_writes = write;
}
}
@ -1042,10 +1051,8 @@ void DirectRenderer::TestState::from_register(GsTest reg) {
}
zte = reg.zte();
if (zte) {
ztst = reg.ztest();
}
}
void DirectRenderer::BlendState::from_register(GsAlpha reg) {
current_register = reg;

View file

@ -207,6 +207,7 @@ class DirectRenderer : public BucketRenderer {
bool wireframe = false;
bool red = false;
bool always_draw = false;
bool disable_mipmap = true;
} m_debug_state;
struct {

View file

@ -73,4 +73,7 @@ ShaderLibrary::ShaderLibrary() {
at(ShaderId::SPRITE_CPU_AFAIL) = {"sprite_cpu_afail"};
at(ShaderId::SKY) = {"sky"};
at(ShaderId::SKY_BLEND) = {"sky_blend"};
at(ShaderId::DEBUG_BUFFERED) = {"debug_buffered"};
at(ShaderId::BUFFERED_TCC0) = {"buffered_tcc0"};
at(ShaderId::BUFFERED_TCC1) = {"buffered_tcc1"};
}

View file

@ -29,8 +29,11 @@ enum class ShaderId {
DEBUG_RED = 4,
SPRITE_CPU = 5,
SPRITE_CPU_AFAIL = 6,
SKY,
SKY_BLEND,
SKY = 7,
SKY_BLEND = 8,
DEBUG_BUFFERED = 9,
BUFFERED_TCC0 = 10,
BUFFERED_TCC1 = 11,
MAX_SHADERS
};

View file

@ -160,7 +160,9 @@ void SpriteRenderer::render_2d_group0(DmaFollower& dma,
auto run = dma.read_and_advance();
assert(run.vifcode0().kind == VifCode::Kind::NOP);
assert(run.vifcode1().kind == VifCode::Kind::MSCAL);
assert(run.vifcode1().immediate == SpriteProgMem::Sprites2dGrp0);
// HACK: this renderers 3D sprites with the 2D renderer. amazingly, it almost works.
// assert(run.vifcode1().immediate == SpriteProgMem::Sprites2dGrp0);
if (m_enabled) {
do_2d_group0_block_cpu(sprite_count, render_state, prof);
}
@ -733,7 +735,7 @@ void SpriteRenderer::do_2d_group0_block_cpu(u32 count,
memset(&packet, 0, sizeof(packet));
// ilw.y vi08, 1(vi02) | nop vi08 = matrix
u32 offset_selector = m_vec_data_2d[sprite_idx].matrix();
assert(offset_selector == 0 || offset_selector == 1);
// assert(offset_selector == 0 || offset_selector == 1);
// moved this out of the loop.
// lq.xyzw vf25, 900(vi00) | nop vf25 = cam_mat
// lq.xyzw vf26, 901(vi00) | nop

View file

@ -0,0 +1,18 @@
#version 330 core
out vec4 color;
in vec4 fragment_color;
in vec3 tex_coord;
uniform sampler2D tex_T0;
uniform float alpha_reject;
void main() {
vec4 T0 = texture(tex_T0, tex_coord.xy / tex_coord.z);
//vec4 T0 = textureProj(tex_T0, vec3(tex_coord.xy, 1.0));
T0.w = 1.0;
color = fragment_color * T0 * 2.0;
if (color.a <= alpha_reject) {
discard;
}
}

View file

@ -0,0 +1,14 @@
#version 330 core
layout (location = 0) in vec3 position_in;
layout (location = 1) in vec4 rgba_in;
layout (location = 2) in vec3 tex_coord_in;
out vec4 fragment_color;
out vec3 tex_coord;
void main() {
gl_Position = vec4((position_in.x - 0.5) * 16. , -(position_in.y - 0.5) * 32, position_in.z, 1.0);
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.a * 2);
tex_coord = tex_coord_in;
}

View file

@ -0,0 +1,17 @@
#version 330 core
out vec4 color;
in vec4 fragment_color;
in vec3 tex_coord;
uniform sampler2D tex_T0;
uniform float alpha_reject;
void main() {
//vec4 T0 = texture(tex_T0, tex_coord);
vec4 T0 = texture(tex_T0, tex_coord.xy / tex_coord.z);
color = fragment_color * T0 * 2.0;
if (color.a <= alpha_reject) {
discard;
}
}

View file

@ -0,0 +1,14 @@
#version 330 core
layout (location = 0) in vec3 position_in;
layout (location = 1) in vec4 rgba_in;
layout (location = 2) in vec3 tex_coord_in;
out vec4 fragment_color;
out vec3 tex_coord;
void main() {
gl_Position = vec4((position_in.x - 0.5) * 16., -(position_in.y - 0.5) * 32, position_in.z, 1.0);
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w * 2.);
tex_coord = tex_coord_in;
}

View file

@ -0,0 +1,9 @@
#version 330 core
out vec4 color;
in vec4 fragment_color;
void main() {
color = fragment_color;
}

View file

@ -0,0 +1,17 @@
// Shader for debugging the buffered renderer.
// no texture
#version 330 core
layout (location = 0) in vec3 position_in;
layout (location = 1) in vec4 rgba_in;
layout (location = 2) in vec3 stq_in;
out vec4 fragment_color;
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, 1.0);
fragment_color = vec4(rgba_in.x, rgba_in.y, rgba_in.z, rgba_in.w + 0.5);
//fragment_color = vec4(1.0, 0, 0, 0.7);
}

View file

@ -0,0 +1,732 @@
#include "BufferedRenderer.h"
#include "common/dma/gs.h"
#include "third-party/imgui/imgui.h"
#include "game/graphics/pipelines/opengl.h"
namespace BufferedRenderer {
std::string DrawMode::to_string() const {
std::string result;
result += fmt::format(" depth-write: {}\n", get_depth_write_enable());
result += fmt::format(" depth-test: ");
switch (get_depth_test()) {
case GsTest::ZTest::NEVER:
result += "never\n";
break;
case GsTest::ZTest::GEQUAL:
result += "gequal\n";
break;
case GsTest::ZTest::ALWAYS:
result += "always\n";
break;
case GsTest::ZTest::GREATER:
result += "greater\n";
break;
default:
assert(false);
}
result += fmt::format(" alpha: ");
switch (get_alpha_blend()) {
case AlphaBlend::SRC_0_SRC_DST:
result += "src, 0, src, dst\n";
break;
case AlphaBlend::SRC_DST_SRC_DST:
result += "src, dst, src, dst\n";
break;
case AlphaBlend::DISABLED:
result += "disabled\n";
break;
default:
assert(false);
}
result += fmt::format(" clamp: {}\n", get_clamp_enable());
result += fmt::format(" filt: {}\n", get_filt_enable());
result += fmt::format(" tcc: {}\n", get_tcc_enable());
result += fmt::format(" aref: {}\n", get_aref());
result += fmt::format(" ate: {}\n", get_at_enable());
result += fmt::format(" atst: ");
switch (get_alpha_test()) {
case AlphaTest::ALWAYS:
result += "always\n";
break;
case AlphaTest::GEQUAL:
result += "gequal\n";
break;
case AlphaTest::NEVER:
result += "never\n";
break;
default:
assert(false);
}
result += fmt::format(" zte: {}\n", get_zt_enable());
result += fmt::format(" abe: {}\n", get_ab_enable());
result += fmt::format(" afail: ");
switch (get_alpha_fail()) {
case GsTest::AlphaFail::KEEP:
result += "keep\n";
break;
case GsTest::AlphaFail::FB_ONLY:
result += "fb-only\n";
break;
case GsTest::AlphaFail::RGB_ONLY:
result += "rgb-only\n";
break;
case GsTest::AlphaFail::ZB_ONLY:
result += "zb-only\n";
break;
default:
assert(false);
}
return result;
}
Renderer::Renderer(BucketId my_id) : m_my_id(my_id) {
glGenBuffers(1, &m_ogl.vertex_buffer);
glGenBuffers(1, &m_ogl.index_buffer);
glGenVertexArrays(1, &m_ogl.vao);
// these are some big buffers
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.vertex_buffer);
m_ogl.vertex_buffer_size = MAX_VERTS;
glBufferData(GL_ARRAY_BUFFER, m_ogl.vertex_buffer_size * sizeof(Vertex), nullptr,
GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer);
m_ogl.index_buffer_size = MAX_VERTS * 3;
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer_size * sizeof(u32), nullptr,
GL_DYNAMIC_DRAW);
}
Renderer::~Renderer() {
glDeleteBuffers(1, &m_ogl.vertex_buffer);
glDeleteBuffers(1, &m_ogl.index_buffer);
glDeleteVertexArrays(1, &m_ogl.vao);
}
void Renderer::render_list(const DrawList& list,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
const std::vector<Vertex>& vertices) {
// first, load primitive buffer
glBindVertexArray(m_ogl.vao);
u32 vert_count = std::min((int)vertices.size(), (int)m_ogl.vertex_buffer_size);
if (vertices.size() > m_ogl.vertex_buffer_size) {
fmt::print("TOO MANY VERTICES: {} / {}\n", vertices.size(), m_ogl.vertex_buffer_size);
assert(false);
}
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.vertex_buffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, vert_count * sizeof(Vertex), vertices.data());
glVertexAttribPointer(0, // location 0 in the shader
3, // 3 values per vert
GL_UNSIGNED_INT, // u32's
GL_TRUE, // normalized
sizeof(Vertex), // stride
(void*)offsetof(Vertex, xyz) // offset (0)
);
glVertexAttribPointer(1, // location 1 in the shader
4, // 4 values per vert
GL_UNSIGNED_BYTE, // u8's
GL_TRUE, // normalized
sizeof(Vertex), // stride
(void*)offsetof(Vertex, rgba) // offset (0)
);
glVertexAttribPointer(2, // location 2 in the shader
3, // 3 values per vert
GL_FLOAT, // u32's
GL_FALSE, // normalized
sizeof(Vertex), // stride
(void*)offsetof(Vertex, st) // offset (0)
);
glActiveTexture(GL_TEXTURE0);
for (auto& group : list.groups) {
if (group.tbp != UINT16_MAX) {
render_group(group, render_state, prof, vertices);
}
}
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glBindVertexArray(0);
}
void Renderer::setup_opengl_excluding_textures(SharedRenderState* render_state, DrawMode mode) {
if (mode.get_depth_write_enable()) {
glDepthMask(GL_TRUE);
} else {
glDepthMask(GL_FALSE);
}
if (mode.get_zt_enable()) {
glEnable(GL_DEPTH_TEST);
switch (mode.get_depth_test()) {
case GsTest::ZTest::NEVER:
glDepthFunc(GL_NEVER);
break;
case GsTest::ZTest::ALWAYS:
glDepthFunc(GL_ALWAYS);
break;
case GsTest::ZTest::GEQUAL:
glDepthFunc(GL_GEQUAL);
break;
case GsTest::ZTest::GREATER:
glDepthFunc(GL_GREATER);
break;
default:
assert(false);
}
} else {
glDisable(GL_DEPTH_TEST);
}
if (mode.get_ab_enable() && mode.get_alpha_blend() != DrawMode::AlphaBlend::DISABLED) {
glEnable(GL_BLEND);
switch (mode.get_alpha_blend()) {
case DrawMode::AlphaBlend::SRC_DST_SRC_DST:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case DrawMode::AlphaBlend::SRC_0_SRC_DST:
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
break;
default:
assert(false);
}
}
if (mode.get_clamp_enable()) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
if (mode.get_filt_enable()) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
float alpha_reject = 0.;
if (mode.get_at_enable()) {
switch (mode.get_alpha_test()) {
case DrawMode::AlphaTest::ALWAYS:
break;
case DrawMode::AlphaTest::GEQUAL:
alpha_reject = mode.get_aref() / 127.f;
break;
case DrawMode::AlphaTest::NEVER:
break;
default:
assert(false);
}
}
// todo check afail
if (mode.get_tcc_enable()) {
render_state->shaders[ShaderId::BUFFERED_TCC1].activate();
glUniform1f(
glGetUniformLocation(render_state->shaders[ShaderId::BUFFERED_TCC1].id(), "alpha_reject"),
alpha_reject);
glUniform1i(glGetUniformLocation(render_state->shaders[ShaderId::BUFFERED_TCC1].id(), "T0"), 0);
} else {
render_state->shaders[ShaderId::BUFFERED_TCC0].activate();
glUniform1f(
glGetUniformLocation(render_state->shaders[ShaderId::BUFFERED_TCC0].id(), "alpha_reject"),
alpha_reject);
glUniform1i(glGetUniformLocation(render_state->shaders[ShaderId::BUFFERED_TCC0].id(), "T0"), 0);
}
}
void Renderer::render_group(const DrawGroup& group,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
const std::vector<Vertex>& /*vertices*/) {
TextureRecord* tex = nullptr;
tex = render_state->texture_pool->lookup(group.tbp);
if (!tex) {
fmt::print("Failed to find texture at {}, using random\n", group.tbp);
tex = render_state->texture_pool->get_random_texture();
}
assert(tex);
// first: do we need to load the texture?
if (!tex->on_gpu) {
render_state->texture_pool->upload_to_gpu(tex);
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex->gpu_texture);
for (auto& draw : group.draws) {
if (draw.mode.is_valid()) {
m_stats.draw_calls++;
prof.add_draw_call();
prof.add_tri(draw.triangles.size());
render_state->shaders[ShaderId::DEBUG_BUFFERED].activate();
setup_opengl_excluding_textures(render_state, draw.mode);
// check for overflows.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer);
if (draw.triangles.size() * 3 > m_ogl.index_buffer_size) {
fmt::format("TOO MANY TRIS: {}/{}\n", draw.triangles.size() * 3, m_ogl.index_buffer_size);
assert(false);
}
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, draw.triangles.size() * sizeof(u32) * 3,
draw.triangles.data());
glDrawElements(GL_TRIANGLES, draw.triangles.size() * 3, GL_UNSIGNED_INT, (void*)0);
}
}
}
void Renderer::draw_debug_window() {
// todo
ImGui::Text("draws: %d", m_stats.draw_calls);
}
void Renderer::clear_stats() {
m_stats = {};
}
Builder::Builder(BucketId my_id) : m_my_id(my_id), m_renderer(my_id) {}
void Builder::add_gif_data_sized(const void* data, u32 expected_size) {
if (expected_size != add_gif_data(data)) {
assert(false); // todo, might be too strict due to alignment crap
}
}
void Builder::flush(SharedRenderState* render_state, ScopedProfilerNode& prof) {
m_renderer.render_list(m_list, render_state, prof, m_vertices);
m_list.clear();
m_vertices.clear();
m_cache = {};
}
void Builder::draw_debug_window() {
ImGui::Text("Builder: %d tri, %d vert", m_stats.m_tri, m_stats.m_dvert);
ImGui::Text("Renderer:");
ImGui::Separator();
m_renderer.draw_debug_window();
}
void Builder::reset_state() {
m_current_mode.as_int() = 0;
m_stats = {};
m_renderer.clear_stats();
m_vertex_queue = {};
m_current_mode.enable_depth_write();
m_current_mode.enable_ab();
m_current_mode.enable_at();
m_current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
}
u32 Builder::add_gif_data(const void* data_in) {
bool eop = false;
auto* data = (const u8*)data_in;
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()) {
handle_prim(tag.prim());
}
for (u32 loop = 0; loop < tag.nloop(); loop++) {
for (u32 reg = 0; reg < nreg; reg++) {
switch (reg_desc[reg]) {
case GifTag::RegisterDescriptor::AD:
handle_ad(data + offset);
break;
case GifTag::RegisterDescriptor::ST:
handle_st_packed(data + offset);
break;
case GifTag::RegisterDescriptor::RGBAQ:
handle_rgbaq_packed(data + offset);
break;
case GifTag::RegisterDescriptor::XYZF2:
handle_xyzf2_packed(data + offset);
break;
// case GifTag::RegisterDescriptor::PRIM:
// handle_prim_packed(data + offset, render_state, prof);
// break;
// case GifTag::RegisterDescriptor::TEX0_1:
// handle_tex0_1_packed(data + offset, render_state, prof);
// break;
default:
fmt::print("Register {} is not supported in packed mode of BufferedRenderer\n",
reg_descriptor_name(reg_desc[reg]));
assert(false);
}
offset += 16; // PACKED = quadwords
}
}
} else if (format == GifTag::Format::REGLIST) {
for (u32 loop = 0; loop < tag.nloop(); loop++) {
for (u32 reg = 0; reg < nreg; reg++) {
u64 register_data;
memcpy(&register_data, data + offset, 8);
// fmt::print("loop: {} reg: {} {}\n", loop, reg,
// reg_descriptor_name(reg_desc[reg]));
switch (reg_desc[reg]) {
// case GifTag::RegisterDescriptor::PRIM:
// handle_prim(register_data, render_state, prof);
// break;
// case GifTag::RegisterDescriptor::RGBAQ:
// handle_rgbaq(register_data);
// break;
// case GifTag::RegisterDescriptor::XYZF2:
// handle_xyzf2(register_data, render_state, prof);
// break;
default:
fmt::print("Register {} is not supported in reglist mode of BufferedRenderer\n",
reg_descriptor_name(reg_desc[reg]));
assert(false);
}
offset += 8; // PACKED = quadwords
}
}
} else {
assert(false); // format not packed or reglist.
}
eop = tag.eop();
}
return offset;
}
void Builder::handle_ad(const u8* data) {
u64 value;
GsRegisterAddress addr;
memcpy(&value, data, sizeof(u64));
memcpy(&addr, data + 8, sizeof(GsRegisterAddress));
switch (addr) {
// case GsRegisterAddress::ZBUF_1:
// handle_zbuf1(value, render_state, prof);
// break;
case GsRegisterAddress::TEST_1:
handle_test1(value);
break;
case GsRegisterAddress::ALPHA_1:
handle_alpha1(value);
break;
// case GsRegisterAddress::PABE:
// handle_pabe(value);
break;
case GsRegisterAddress::CLAMP_1:
handle_clamp1(value);
break;
// case GsRegisterAddress::PRIM:
// handle_prim(value, render_state, prof);
// break;
//
case GsRegisterAddress::TEX1_1:
handle_tex1_1(value);
break;
// case GsRegisterAddress::TEXA:
// handle_texa(value);
// break;
// case GsRegisterAddress::TEXCLUT:
// // TODO
// // the only thing the direct renderer does with texture is font, which does no tricks
// with
// // CLUT. The texture upload process will do all of the lookups with the default CLUT.
// // So we'll just assume that the TEXCLUT is set properly and ignore this.
// break;
// case GsRegisterAddress::FOGCOL:
// // TODO
// break;
case GsRegisterAddress::TEX0_1:
handle_tex0_1(value);
break;
case GsRegisterAddress::MIPTBP1_1:
case GsRegisterAddress::MIPTBP2_1:
// this has the address of different mip levels, we can just ignore it.
break;
// case GsRegisterAddress::TEXFLUSH:
// break;
default:
fmt::print("Address {} is not supported in ad of BufferedRenderer\n",
register_address_name(addr));
assert(false);
}
}
void Builder::handle_test1(u64 val) {
// ate, atst, aref, afail, date, datm, zte, ztest
GsTest test(val);
// ATE
m_current_mode.set_at(test.alpha_test_enable());
// ATST
switch (test.alpha_test()) {
case GsTest::AlphaTest::ALWAYS:
m_current_mode.set_alpha_test(DrawMode::AlphaTest::ALWAYS);
break;
case GsTest::AlphaTest::GEQUAL:
m_current_mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL);
break;
case GsTest::AlphaTest::NEVER:
m_current_mode.set_alpha_test(DrawMode::AlphaTest::NEVER);
break;
default:
fmt::print("Alpha test: {} not supported\n", (int)test.alpha_test());
assert(false);
}
// AREF
m_current_mode.set_aref(test.aref());
// AFAIL
m_current_mode.set_alpha_fail(test.afail());
// DATE
assert(test.date() == false);
// DATM
// who cares, if date is off
// ZTE
m_current_mode.set_zt(test.zte());
// ZTST
m_current_mode.set_depth_test(test.ztest());
}
void Builder::handle_tex0_1(u64 val) {
GsTex0 reg(val);
// TBP0
m_current_tbp = reg.tbp0();
// TBW
// assume it's right
// PSM
assert(reg.psm() != GsTex0::PSM::PSMT4HH); // not supported in buffered yet.
// TW, TH
// assume it's right
// TCC
m_current_mode.set_tcc(reg.tcc());
// TFX
assert(reg.tfx() == GsTex0::TextureFunction::MODULATE);
// CBP, CPSM, CSM
// assume it's right
}
void Builder::handle_tex1_1(u64 val) {
GsTex1 reg(val);
// ignoring these and doing our own thing!
// just need to pick between filtering and not filtering.
m_current_mode.set_filt_enable(reg.mmag());
}
void Builder::handle_clamp1(u64 val) {
// check that we got one of the expected ones
if (!(val == 0b101 || val == 0 || val == 1 || val == 0b100)) {
fmt::print("clamp: 0x{:x}\n", val);
assert(false);
}
// this isn't quite right, but I'm hoping it's enough!
m_current_mode.set_clamp_enable(val == 0b101);
}
void Builder::handle_alpha1(u64 val) {
GsAlpha reg(val);
if (reg.a_mode() == GsAlpha::BlendMode::SOURCE && reg.b_mode() == GsAlpha::BlendMode::DEST &&
reg.c_mode() == GsAlpha::BlendMode::SOURCE && reg.d_mode() == GsAlpha::BlendMode::DEST) {
// (Cs - Cd) * As + Cd
// Cs * As + (1 - As) * Cd
m_current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
} else if (reg.a_mode() == GsAlpha::BlendMode::SOURCE &&
reg.b_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
reg.c_mode() == GsAlpha::BlendMode::SOURCE &&
reg.d_mode() == GsAlpha::BlendMode::DEST) {
// (Cs - 0) * As + Cd
// Cs * As + (1) * CD
m_current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_SRC_DST);
} else {
// unsupported blend: a 0 b 2 c 2 d 1
fmt::print("unsupported blend: a {} b {} c {} d {}\n", (int)reg.a_mode(), (int)reg.b_mode(),
(int)reg.c_mode(), (int)reg.d_mode());
assert(false);
}
}
void Builder::handle_prim(u64 val) {
GsPrim prim(val);
// PRIM
m_prim_kind = prim.kind();
// IIP
assert(prim.gouraud());
// TME
assert(prim.tme());
// FGE
// TODO fog
// ABE
m_current_mode.set_ab(prim.abe());
// AA1
assert(!prim.aa1());
// FST
assert(!prim.fst());
// CTXT
assert(!prim.ctxt());
// FIX
assert(!prim.fix());
if (m_vertex_queue.startup) {
m_vertex_queue.startup = 0;
m_vertex_queue.idx = 0;
} else {
// assert(false);
}
}
void Builder::handle_st_packed(const u8* data) {
memcpy(m_st_pending_q, data, 3 * sizeof(float));
}
void Builder::handle_rgbaq_packed(const u8* data) {
m_q = m_st_pending_q[2];
// memcpy(m_rgba.data(), data, 4);
m_rgba[0] = data[0];
m_rgba[1] = data[4];
m_rgba[2] = data[8];
m_rgba[3] = data[12];
}
void Builder::handle_xyzf2_packed(const u8* data) {
u32 x, y;
memcpy(&x, data, 4);
memcpy(&y, data + 4, 4);
u64 upper;
memcpy(&upper, data + 8, 8);
u32 z = (upper >> 4) & 0xffffff;
u8 f = (upper >> 36);
bool adc = upper & (1ull << 47);
handle_xyzf2_common(x, y, z, f, !adc);
}
void Builder::handle_xyzf2_common(u32 x, u32 y, u32 z, u8 /*f*/, bool advance) {
// first, create a vertex:
u32 new_vertex = create_vertex_now(x, y, z);
// next, add that vertex. This will inform us if we actually draw a prim or not
bool new_prim = false;
switch (m_prim_kind) {
case GsPrim::Kind::TRI_STRIP:
new_prim = handle_tri_strip_add(new_vertex, advance);
break;
default:
assert(false);
}
if (new_prim) {
// todo, winding order?
add_prim_now({m_vertex_queue.verts[0], m_vertex_queue.verts[1], m_vertex_queue.verts[2]});
}
}
void Builder::add_prim_now(Triangle tri) {
if (m_cache.last_tbp == m_current_tbp && m_cache.last_mode == m_current_mode.as_int()) {
m_cache.draw->triangles.push_back(tri);
} else {
auto group = m_list.get_group_for_tbp(m_current_tbp);
assert(group);
// todo flush and stats
auto draw = group->get_draw_for_mode(m_current_mode);
assert(draw);
// todo flush and stats
draw->triangles.push_back(tri);
m_cache.last_mode = m_current_mode.as_int();
m_cache.last_tbp = m_current_tbp;
m_cache.draw = draw;
}
m_stats.m_tri++;
}
u32 Builder::create_vertex_now(u32 x, u32 y, u32 z) {
m_stats.m_dvert++;
u32 idx = m_vertices.size();
m_vertices.emplace_back();
auto& vert = m_vertices.back();
vert.xyz = math::Vector<u32, 3>(x << 16, y << 16, z << 8);
vert.rgba = m_rgba;
vert.st = math::Vector<float, 2>(m_st_pending_q[0], m_st_pending_q[1]);
vert.q = m_q;
return idx;
}
bool Builder::handle_tri_strip_add(u32 new_vertex, bool advance) {
m_vertex_queue.verts[m_vertex_queue.idx] = new_vertex;
m_vertex_queue.idx++;
// wrap the index
if (m_vertex_queue.idx == 3) {
m_vertex_queue.idx = 0;
}
// bump the startup
if (m_vertex_queue.startup < 3) {
m_vertex_queue.startup++;
}
if (m_vertex_queue.startup >= 3) {
if (advance) {
return true;
}
}
return false;
}
} // namespace BufferedRenderer

View file

@ -0,0 +1,347 @@
#pragma once
#include "common/math/Vector.h"
#include "common/common_types.h"
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "common/dma/gs.h"
#include "game/graphics/pipelines/opengl.h"
namespace BufferedRenderer {
// The buffered renderer performs efficient sorting of primitives to reduce draw calls.
// the settings:
// this is the maximum number of draw "kinds" we can have per textures.
// a draw is a different "kind" if any opengl state changes happen (like z test, etc)
// if this is exceeded, this will return an error code.
// you must flush the group, then try adding it again.
// making this too large will slow down insertion and increase memory usage
constexpr int MAX_DRAW_KINDS_PER_TEX = 4;
// this is the maximum number of textures. If this is exceeded, there will be an error like above.
constexpr int MAX_TEXTURES = 256;
// this is the PS2 maximum TBP value.
constexpr int MAX_TBP = 16384;
// 32-byte vertex.
// the xyz, rgba, and stq are aligned. we have a free 4-bytes at the end.
// there is a single big pool of vertices.
struct Vertex {
math::Vector<u32, 3> xyz; // ps2 coords (0)
math::Vector<u8, 4> rgba; // 0, 4 (1)
math::Vector<float, 2> st; // (2)
float q; // (3)
u32 pad;
};
static_assert(sizeof(Vertex) == 32);
// a triangle grabs three vertices from the pools.
struct Triangle {
u32 verts[3];
};
// this represents all of the drawing state, stored as an integer.
// it can also represent "invalid".
class DrawMode {
public:
enum class AlphaBlend {
DISABLED = 0,
SRC_DST_SRC_DST = 1,
SRC_0_SRC_DST = 2,
};
enum class AlphaTest {
NEVER = 0,
ALWAYS = 1,
GEQUAL = 2,
};
bool get_depth_write_enable() const { return m_val & 0b1; }
void enable_depth_write() { m_val = m_val | 0b1; }
void disable_depth_write() { m_val = m_val & ~(0b1); }
GsTest::ZTest get_depth_test() const { return (GsTest::ZTest)((m_val >> 1) & 0b11); }
void set_depth_test(GsTest::ZTest dt) { m_val = (m_val & ~(0b110)) | ((u32)(dt) << 1); }
AlphaBlend get_alpha_blend() const { return (AlphaBlend)((m_val >> 3) & 0b11); }
void set_alpha_blend(AlphaBlend ab) { m_val = (m_val & ~(0b11000)) | ((u32)(ab) << 3); }
u8 get_aref() const { return m_val >> 8; }
void set_aref(u8 val) { m_val = (m_val & ~(0xff00)) | (val << 8); }
AlphaTest get_alpha_test() const { return (AlphaTest)((m_val >> 16) & 0b11); }
void set_alpha_test(AlphaTest ab) { m_val = (m_val & ~(0b11 << 16)) | ((u32)(ab) << 16); }
GsTest::AlphaFail get_alpha_fail() const { return (GsTest::AlphaFail)((m_val >> 21) & 0b11); }
void set_alpha_fail(GsTest::AlphaFail ab) { m_val = (m_val & ~(0b11 << 21)) | ((u32)(ab) << 21); }
bool is_invalid() const { return m_val == UINT32_MAX; }
bool is_valid() const { return !is_invalid(); }
void set_invalid() { m_val = UINT32_MAX; }
bool get_clamp_enable() const { return m_val & (1 << 5); }
void set_clamp_enable(bool en) {
if (en) {
enable_clamp();
} else {
disable_clamp();
}
}
void enable_clamp() { m_val = m_val | (1 << 5); }
void disable_clamp() { m_val = m_val & (~(1 << 5)); }
bool get_filt_enable() const { return m_val & (1 << 6); }
void enable_filt() { m_val = m_val | (1 << 6); }
void disable_filt() { m_val = m_val & (~(1 << 6)); }
void set_filt_enable(bool en) {
if (en) {
enable_filt();
} else {
disable_filt();
}
}
bool get_tcc_enable() const { return m_val & (1 << 6); }
void enable_tcc() { m_val = m_val | (1 << 7); }
void disable_tcc() { m_val = m_val & (~(1 << 7)); }
void set_tcc(bool en) {
if (en) {
enable_tcc();
} else {
disable_tcc();
}
}
bool get_at_enable() const { return m_val & (1 << 18); }
void enable_at() { m_val = m_val | (1 << 18); }
void disable_at() { m_val = m_val & (~(1 << 18)); }
void set_at(bool en) {
if (en) {
enable_at();
} else {
disable_at();
}
}
bool get_zt_enable() const { return m_val & (1 << 19); }
void enable_zt() { m_val = m_val | (1 << 19); }
void disable_zt() { m_val = m_val & (~(1 << 19)); }
void set_zt(bool en) {
if (en) {
enable_zt();
} else {
disable_zt();
}
}
bool get_ab_enable() const { return m_val & (1 << 20); }
void enable_ab() { m_val = m_val | (1 << 20); }
void disable_ab() { m_val = m_val & (~(1 << 20)); }
void set_ab(bool en) {
if (en) {
enable_ab();
} else {
disable_ab();
}
}
u32& as_int() { return m_val; }
bool operator==(const DrawMode& other) const { return m_val == other.m_val; }
bool operator!=(const DrawMode& other) const { return m_val != other.m_val; }
std::string to_string() const;
private:
// 0 - depth write enable
// 1, 2 - test: never, always, gequal, greater
// 3, 4 - alpha: disable, [src,dst,src,dst], [src,0,src,dst], XX
// 5 - clamp enable
// 6 - filt enable
// 7 - tcc enable
// 8,9,10,11,12,14,14,15 - aref
// 16, 17 - atest
// 18 - ate
// 19 - zte
// 20 - abe
// 21, 22 - afail
u32 m_val = UINT32_MAX;
};
struct Draw {
DrawMode mode;
std::vector<Triangle> triangles; // just indices
void clear() {
mode.set_invalid();
triangles.clear();
}
};
struct DrawGroup {
Draw draws[MAX_DRAW_KINDS_PER_TEX];
u16 tbp = UINT16_MAX;
// can fail, in which case you should flush the DrawGroup.
Draw* get_draw_for_mode(DrawMode mode) {
for (auto& draw : draws) {
if (draw.mode.is_invalid()) {
draw.mode = mode;
return &draw;
} else if (draw.mode == mode) {
return &draw;
}
}
return nullptr;
}
void clear() {
for (auto& draw : draws) {
draw.clear();
tbp = UINT16_MAX;
}
}
};
struct DrawList {
DrawGroup groups[MAX_TEXTURES];
u16 tbp_to_tex_id[MAX_TBP];
u16 current_tex_id = 0;
DrawList() {
for (auto& x : tbp_to_tex_id) {
x = UINT16_MAX;
}
}
void clear() {
for (auto& group : groups) {
group.clear();
}
for (auto& x : tbp_to_tex_id) {
x = UINT16_MAX;
}
current_tex_id = 0;
}
DrawGroup* get_group_for_tbp(u16 tbp) {
if (tbp_to_tex_id[tbp] != UINT16_MAX) {
// already have it
return &groups[tbp_to_tex_id[tbp]];
} else {
if (current_tex_id == MAX_TEXTURES) {
// don't have it, and out of room
return nullptr;
} else {
// don't have it, but we can add it.
auto group = &groups[current_tex_id];
group->tbp = tbp;
tbp_to_tex_id[tbp] = current_tex_id;
current_tex_id++;
return group;
}
}
}
};
class Renderer {
public:
Renderer(BucketId my_id);
~Renderer();
void render_list(const DrawList& list,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
const std::vector<Vertex>& vertices);
void render_group(const DrawGroup& group,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
const std::vector<Vertex>& vertices);
void setup_opengl_excluding_textures(SharedRenderState* render_state, DrawMode mode);
void draw_debug_window();
void clear_stats();
BucketId my_id() const { return m_my_id; }
private:
static constexpr int MAX_VERTS = 400000;
BucketId m_my_id;
struct {
int triangles = 0;
int draw_calls = 0;
int groups = 0;
} m_stats;
struct {
GLuint vertex_buffer = -1;
u32 vertex_buffer_size = 0;
GLuint index_buffer = -1;
u32 index_buffer_size = 0;
GLuint vao = -1;
} m_ogl;
};
class Builder {
public:
Builder(BucketId my_id);
u32 add_gif_data(const void* data);
void add_gif_data_sized(const void* data, u32 expected_size);
void flush(SharedRenderState* render_state, ScopedProfilerNode& prof);
void draw_debug_window();
void reset_state();
private:
void handle_ad(const u8* data);
void handle_test1(u64 val);
void handle_tex0_1(u64 val);
void handle_tex1_1(u64 val);
void handle_clamp1(u64 val);
void handle_prim(u64 val);
void handle_alpha1(u64 val);
void handle_st_packed(const u8* data);
void handle_rgbaq_packed(const u8* data);
void handle_xyzf2_packed(const u8* data);
void handle_xyzf2_common(u32 x, u32 y, u32 z, u8 f, bool advance);
bool handle_tri_strip_add(u32 new_vertex, bool advance);
void add_prim_now(Triangle tri);
u32 create_vertex_now(u32 x, u32 y, u32 z);
BucketId my_id() const { return m_my_id; }
BucketId m_my_id;
DrawList m_list;
Renderer m_renderer;
DrawMode m_current_mode;
u16 m_current_tbp = 0;
GsPrim::Kind m_prim_kind = GsPrim::Kind::PRIM_7;
std::vector<Vertex> m_vertices;
float m_st_pending_q[3] = {0}; // q goes to real q on rgbaq packed
float m_q = 0;
math::Vector<u8, 4> m_rgba;
// todo maybe add a mode cache?
struct {
u32 idx = 0;
u32 startup = 0;
u32 verts[3] = {0, 0, 0};
} m_vertex_queue;
struct {
u32 m_dvert = 0;
u32 m_tri = 0;
} m_stats;
struct {
u32 last_tbp = UINT32_MAX;
u32 last_mode = UINT32_MAX;
Draw* draw = nullptr;
} m_cache;
};
} // namespace BufferedRenderer

View file

@ -18,7 +18,8 @@ bool looks_like_tfrag_init(const DmaFollower& follow) {
TFragment::TFragment(const std::string& name, BucketId my_id, bool child_mode)
: BucketRenderer(name, my_id),
m_child_mode(child_mode),
m_direct_renderer(fmt::format("{}.direct", name), my_id, 1024, DirectRenderer::Mode::NORMAL) {
m_direct_renderer(fmt::format("{}.direct", name), my_id, 1024, DirectRenderer::Mode::NORMAL),
m_buffered_renderer(my_id) {
for (auto& buf : m_buffered_data) {
for (auto& x : buf.pad) {
x = 0xff;
@ -35,7 +36,12 @@ void TFragment::render(DmaFollower& dma,
ScopedProfilerNode& prof) {
m_debug_string.clear();
m_frag_debug.clear();
if (m_use_buffered_renderer) {
m_buffered_renderer.reset_state();
} else {
m_direct_renderer.reset_state();
}
m_stats = {};
if (!m_enabled) {
@ -101,7 +107,12 @@ void TFragment::render(DmaFollower& dma,
}
m_debug_string += fmt::format("fail: {}\n", dma.current_tag().print());
if (m_use_buffered_renderer) {
m_buffered_renderer.flush(render_state, prof);
} else {
m_direct_renderer.flush_pending(render_state, prof);
}
while (dma.current_tag_offset() != render_state->next_bucket) {
auto tag = dma.current_tag().print();
@ -124,6 +135,7 @@ void TFragment::draw_debug_window() {
ImGui::Checkbox("Prog10 hack", &m_prog10_with_prog6);
ImGui::Checkbox("Prog18 hack", &m_prog18_with_prog6);
ImGui::Checkbox("Others with prog6", &m_all_with_prog6);
ImGui::Checkbox("Use Buffered Renderer", &m_use_buffered_renderer);
ImGui::Text("packets: %d", m_stats.tfrag_dma_packets);
ImGui::Text("frag bytes: %d", m_stats.tfrag_bytes);
ImGui::Text("errors: %d", m_stats.error_packets);
@ -131,11 +143,16 @@ void TFragment::draw_debug_window() {
ImGui::Text(" prog %d: %d calls\n", prog, m_stats.per_program[prog].calls);
}
if (ImGui::TreeNode("direct")) {
if (!m_use_buffered_renderer && ImGui::TreeNode("direct")) {
m_direct_renderer.draw_debug_window();
ImGui::TreePop();
}
if (m_use_buffered_renderer && ImGui::TreeNode("buffered")) {
m_buffered_renderer.draw_debug_window();
ImGui::TreePop();
}
ImGui::TextUnformatted(m_debug_string.data());
}
@ -149,7 +166,11 @@ void TFragment::handle_initialization(DmaFollower& dma,
assert(setup_test.vifcode1().immediate == 2);
assert(setup_test.size_bytes == 32);
memcpy(m_test_setup, setup_test.data, 32);
if (m_use_buffered_renderer) {
m_buffered_renderer.add_gif_data_sized(m_test_setup, 32);
} else {
m_direct_renderer.render_gif(m_test_setup, 32, render_state, prof);
}
// matrix 0
auto mat0_upload = dma.read_and_advance();

View file

@ -2,6 +2,7 @@
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/opengl_renderer/DirectRenderer.h"
#include "game/graphics/opengl_renderer/tfrag/BufferedRenderer.h"
#include "common/dma/gs.h"
#include "common/math/Vector.h"
@ -191,6 +192,7 @@ class TFragment : public BucketRenderer {
bool m_prog10_with_prog6 = true;
bool m_prog18_with_prog6 = true;
bool m_all_with_prog6 = false;
bool m_use_buffered_renderer = true;
std::string m_frag_debug;
// GS setup data
@ -278,4 +280,5 @@ class TFragment : public BucketRenderer {
} m_stats;
DirectRenderer m_direct_renderer;
BufferedRenderer::Builder m_buffered_renderer;
};

View file

@ -1162,10 +1162,15 @@ void TFragment::XGKICK(u32 addr, SharedRenderState* render_state, ScopedProfiler
assert(addr < KICK_ZONE_END);
if (!m_skip_xgkick) {
if (m_use_buffered_renderer) {
m_buffered_renderer.add_gif_data(
&m_kick_data.pad[(addr - TFragDataMem::TFragKickZoneData) * 16]);
} else {
m_direct_renderer.render_gif(&m_kick_data.pad[(addr - TFragDataMem::TFragKickZoneData) * 16],
UINT32_MAX, render_state, prof);
}
}
}
template void TFragment::exec_program_6<true>(SharedRenderState* render_state,
ScopedProfilerNode& prof);

View file

@ -139,6 +139,7 @@ static std::shared_ptr<GfxDisplay> gl_make_main_display(int width,
}
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
std::string image_path = fmt::format("{}/docs/favicon-nobg.png", file_util::get_project_path());

View file

@ -60,10 +60,6 @@ void TextureRecord::serialize(Serializer& ser) {
ser.from_ptr(&gpu_texture);
ser.from_ptr(&dest);
ser.from_pod_vector(&data);
ser.from_ptr(&min_a_zero);
ser.from_ptr(&max_a_zero);
ser.from_ptr(&min_a_nonzero);
ser.from_ptr(&max_a_nonzero);
if (ser.is_loading()) {
gpu_texture = -1;
@ -242,10 +238,6 @@ std::vector<std::shared_ptr<TextureRecord>> TexturePool::convert_textures(const
min_a_zero = std::min(min_a_zero, a);
}
}
texture_record->max_a_zero = max_a_zero;
texture_record->min_a_zero = min_a_zero;
texture_record->max_a_nonzero = max_a_nonzero;
texture_record->min_a_nonzero = min_a_nonzero;
// Debug output.
if (dump_textures_to_file) {
@ -409,6 +401,12 @@ void TexturePool::upload_to_gpu(TextureRecord* tex) {
// we have to set these, imgui won't do it automatically
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex->gpu_texture);
glGenerateMipmap(GL_TEXTURE_2D);
float aniso = 0.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &aniso);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, aniso);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

View file

@ -7,22 +7,55 @@
#include "game/graphics/texture/TextureConverter.h"
#include "common/util/Serializer.h"
// Converting textures happens when textures are uploaded by the game.
// Uploading textures to the gpu and creating samplers is done lazily, as needed.
// Each sampler
struct TextureSampler {
u64 sampler_object = -1; // the opengl sampler
u64 handle = -1; // the handle used for bindless textures.
bool created = false; // lazily created as needed, by default we don't make them
};
// Each texture in our pool has a record:
struct TextureRecord {
std::string page_name;
std::string name;
u8 mip_level;
u8 psm = -1;
u8 cpsm = -1;
u16 w, h;
u8 data_segment;
bool on_gpu = false;
bool do_gc = true; // if set, will be unloaded from GPU when another is upload on top
std::string page_name; // the page we belong to (from game info)
std::string name; // our name (from game info)
u8 mip_level; // which mip we are
u8 psm = -1; // format in the game
u8 cpsm = -1; // clut format in the game
u16 w, h; // our dimensions
u8 data_segment; // which segment we came from in the texture page
bool on_gpu = false; // if we are uploaded to the GPU
// garbage collection settings.
// by default, do_gc is set, and the pool will take care of freeing textures.
// when a texture is uploaded on top of a texture (in PS2 VRAM), the texture will be unloaded from
// the GPU. Unless somebody has another instance of the shared_ptr to this texture, this structure
// (including converted texture data) will be discarded.
// In some cases, this is not desirable because the game may toggle between two different textures
// in VRAM, and we don't want to reconvert every time. The TextureUploadHandler will implement its
// own caching to help with this. To manage textures yourself, you should:
// - keep around a shared_ptr to the TextureRecord (so it doesn't get deleted when it's out of PS2
// VRAM).
// - set do_gc to false (to keep texture in GPU memory when replaced).
// In this case, you must use the discard function to remove the texture from the GPU, if you
// really want to get rid of it.
bool do_gc = true;
// The texture data. In some cases, we keep textures only on the GPU (for example, the result of a
// render to texture). In these, data is not populated, but you must set only_on_gpu = true. When
// saving graphics state, the texture will be dumped from the GPU and saved to the file so it is
// possible to restore.
bool only_on_gpu = false;
std::vector<u8> data;
u64 gpu_texture = 0;
u32 dest = -1;
u8 min_a_zero, max_a_zero, min_a_nonzero, max_a_nonzero;
// if we're on the gpu, our OpenGL texture
u64 gpu_texture = 0;
// our VRAM address.
u32 dest = -1;
void unload_from_gpu();

View file

@ -119,7 +119,7 @@ int IsPressed(MappingInfo& mapping, Button button, int pad = 0) {
// returns the value of the analog axis (in the future, likely pressure sensitive if we support it?)
// if invalid or otherwise -- returns 127 (analog stick neutral position)
int AnalogValue(MappingInfo& mapping, Analog analog, int pad = 0) {
int AnalogValue(MappingInfo& /*mapping*/, Analog analog, int pad = 0) {
if (CheckPadIdx(pad) == -1) {
return 127;
}

68
third-party/glad/include/glad/glad.h generated vendored
View file

@ -1,22 +1,23 @@
/*
OpenGL loader generated by glad 0.1.34 on Wed Oct 6 20:57:47 2021.
OpenGL loader generated by glad 0.1.34 on Sun Nov 14 17:13:20 2021.
Language/Generator: C/C++
Specification: gl
APIs: gl=4.3
Profile: compatibility
Extensions:
GL_ARB_bindless_texture,
GL_ARB_texture_filter_anisotropic
Loader: True
Local files: False
Omit khrplatform: False
Reproducible: False
Commandline:
--profile="compatibility" --api="gl=4.3" --generator="c" --spec="gl" --extensions=""
--profile="compatibility" --api="gl=4.3" --generator="c" --spec="gl" --extensions="GL_ARB_bindless_texture,GL_ARB_texture_filter_anisotropic"
Online:
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D4.3
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D4.3&extensions=GL_ARB_bindless_texture&extensions=GL_ARB_texture_filter_anisotropic
*/
@ -4669,6 +4670,65 @@ typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void *ptr, GLsizei buf
GLAPI PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel;
#define glGetObjectPtrLabel glad_glGetObjectPtrLabel
#endif
#define GL_UNSIGNED_INT64_ARB 0x140F
#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE
#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF
#ifndef GL_ARB_bindless_texture
#define GL_ARB_bindless_texture 1
GLAPI int GLAD_GL_ARB_bindless_texture;
typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC)(GLuint texture);
GLAPI PFNGLGETTEXTUREHANDLEARBPROC glad_glGetTextureHandleARB;
#define glGetTextureHandleARB glad_glGetTextureHandleARB
typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC)(GLuint texture, GLuint sampler);
GLAPI PFNGLGETTEXTURESAMPLERHANDLEARBPROC glad_glGetTextureSamplerHandleARB;
#define glGetTextureSamplerHandleARB glad_glGetTextureSamplerHandleARB
typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC)(GLuint64 handle);
GLAPI PFNGLMAKETEXTUREHANDLERESIDENTARBPROC glad_glMakeTextureHandleResidentARB;
#define glMakeTextureHandleResidentARB glad_glMakeTextureHandleResidentARB
typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC)(GLuint64 handle);
GLAPI PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC glad_glMakeTextureHandleNonResidentARB;
#define glMakeTextureHandleNonResidentARB glad_glMakeTextureHandleNonResidentARB
typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC)(GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format);
GLAPI PFNGLGETIMAGEHANDLEARBPROC glad_glGetImageHandleARB;
#define glGetImageHandleARB glad_glGetImageHandleARB
typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC)(GLuint64 handle, GLenum access);
GLAPI PFNGLMAKEIMAGEHANDLERESIDENTARBPROC glad_glMakeImageHandleResidentARB;
#define glMakeImageHandleResidentARB glad_glMakeImageHandleResidentARB
typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC)(GLuint64 handle);
GLAPI PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC glad_glMakeImageHandleNonResidentARB;
#define glMakeImageHandleNonResidentARB glad_glMakeImageHandleNonResidentARB
typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC)(GLint location, GLuint64 value);
GLAPI PFNGLUNIFORMHANDLEUI64ARBPROC glad_glUniformHandleui64ARB;
#define glUniformHandleui64ARB glad_glUniformHandleui64ARB
typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC)(GLint location, GLsizei count, const GLuint64 *value);
GLAPI PFNGLUNIFORMHANDLEUI64VARBPROC glad_glUniformHandleui64vARB;
#define glUniformHandleui64vARB glad_glUniformHandleui64vARB
typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC)(GLuint program, GLint location, GLuint64 value);
GLAPI PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC glad_glProgramUniformHandleui64ARB;
#define glProgramUniformHandleui64ARB glad_glProgramUniformHandleui64ARB
typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 *values);
GLAPI PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC glad_glProgramUniformHandleui64vARB;
#define glProgramUniformHandleui64vARB glad_glProgramUniformHandleui64vARB
typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC)(GLuint64 handle);
GLAPI PFNGLISTEXTUREHANDLERESIDENTARBPROC glad_glIsTextureHandleResidentARB;
#define glIsTextureHandleResidentARB glad_glIsTextureHandleResidentARB
typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC)(GLuint64 handle);
GLAPI PFNGLISIMAGEHANDLERESIDENTARBPROC glad_glIsImageHandleResidentARB;
#define glIsImageHandleResidentARB glad_glIsImageHandleResidentARB
typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC)(GLuint index, GLuint64EXT x);
GLAPI PFNGLVERTEXATTRIBL1UI64ARBPROC glad_glVertexAttribL1ui64ARB;
#define glVertexAttribL1ui64ARB glad_glVertexAttribL1ui64ARB
typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC)(GLuint index, const GLuint64EXT *v);
GLAPI PFNGLVERTEXATTRIBL1UI64VARBPROC glad_glVertexAttribL1ui64vARB;
#define glVertexAttribL1ui64vARB glad_glVertexAttribL1ui64vARB
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC)(GLuint index, GLenum pname, GLuint64EXT *params);
GLAPI PFNGLGETVERTEXATTRIBLUI64VARBPROC glad_glGetVertexAttribLui64vARB;
#define glGetVertexAttribLui64vARB glad_glGetVertexAttribLui64vARB
#endif
#ifndef GL_ARB_texture_filter_anisotropic
#define GL_ARB_texture_filter_anisotropic 1
GLAPI int GLAD_GL_ARB_texture_filter_anisotropic;
#endif
#ifdef __cplusplus
}

50
third-party/glad/src/glad.c generated vendored
View file

@ -1,22 +1,23 @@
/*
OpenGL loader generated by glad 0.1.34 on Wed Oct 6 20:57:47 2021.
OpenGL loader generated by glad 0.1.34 on Sun Nov 14 17:13:20 2021.
Language/Generator: C/C++
Specification: gl
APIs: gl=4.3
Profile: compatibility
Extensions:
GL_ARB_bindless_texture,
GL_ARB_texture_filter_anisotropic
Loader: True
Local files: False
Omit khrplatform: False
Reproducible: False
Commandline:
--profile="compatibility" --api="gl=4.3" --generator="c" --spec="gl" --extensions=""
--profile="compatibility" --api="gl=4.3" --generator="c" --spec="gl" --extensions="GL_ARB_bindless_texture,GL_ARB_texture_filter_anisotropic"
Online:
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D4.3
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D4.3&extensions=GL_ARB_bindless_texture&extensions=GL_ARB_texture_filter_anisotropic
*/
#include <stdio.h>
@ -1184,6 +1185,24 @@ PFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL;
PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL;
PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL;
PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL;
int GLAD_GL_ARB_bindless_texture = 0;
int GLAD_GL_ARB_texture_filter_anisotropic = 0;
PFNGLGETTEXTUREHANDLEARBPROC glad_glGetTextureHandleARB = NULL;
PFNGLGETTEXTURESAMPLERHANDLEARBPROC glad_glGetTextureSamplerHandleARB = NULL;
PFNGLMAKETEXTUREHANDLERESIDENTARBPROC glad_glMakeTextureHandleResidentARB = NULL;
PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC glad_glMakeTextureHandleNonResidentARB = NULL;
PFNGLGETIMAGEHANDLEARBPROC glad_glGetImageHandleARB = NULL;
PFNGLMAKEIMAGEHANDLERESIDENTARBPROC glad_glMakeImageHandleResidentARB = NULL;
PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC glad_glMakeImageHandleNonResidentARB = NULL;
PFNGLUNIFORMHANDLEUI64ARBPROC glad_glUniformHandleui64ARB = NULL;
PFNGLUNIFORMHANDLEUI64VARBPROC glad_glUniformHandleui64vARB = NULL;
PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC glad_glProgramUniformHandleui64ARB = NULL;
PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC glad_glProgramUniformHandleui64vARB = NULL;
PFNGLISTEXTUREHANDLERESIDENTARBPROC glad_glIsTextureHandleResidentARB = NULL;
PFNGLISIMAGEHANDLERESIDENTARBPROC glad_glIsImageHandleResidentARB = NULL;
PFNGLVERTEXATTRIBL1UI64ARBPROC glad_glVertexAttribL1ui64ARB = NULL;
PFNGLVERTEXATTRIBL1UI64VARBPROC glad_glVertexAttribL1ui64vARB = NULL;
PFNGLGETVERTEXATTRIBLUI64VARBPROC glad_glGetVertexAttribLui64vARB = NULL;
static void load_GL_VERSION_1_0(GLADloadproc load) {
if(!GLAD_GL_VERSION_1_0) return;
glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
@ -2150,9 +2169,29 @@ static void load_GL_VERSION_4_3(GLADloadproc load) {
glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load("glGetObjectPtrLabel");
glad_glGetPointerv = (PFNGLGETPOINTERVPROC)load("glGetPointerv");
}
static void load_GL_ARB_bindless_texture(GLADloadproc load) {
if(!GLAD_GL_ARB_bindless_texture) return;
glad_glGetTextureHandleARB = (PFNGLGETTEXTUREHANDLEARBPROC)load("glGetTextureHandleARB");
glad_glGetTextureSamplerHandleARB = (PFNGLGETTEXTURESAMPLERHANDLEARBPROC)load("glGetTextureSamplerHandleARB");
glad_glMakeTextureHandleResidentARB = (PFNGLMAKETEXTUREHANDLERESIDENTARBPROC)load("glMakeTextureHandleResidentARB");
glad_glMakeTextureHandleNonResidentARB = (PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC)load("glMakeTextureHandleNonResidentARB");
glad_glGetImageHandleARB = (PFNGLGETIMAGEHANDLEARBPROC)load("glGetImageHandleARB");
glad_glMakeImageHandleResidentARB = (PFNGLMAKEIMAGEHANDLERESIDENTARBPROC)load("glMakeImageHandleResidentARB");
glad_glMakeImageHandleNonResidentARB = (PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC)load("glMakeImageHandleNonResidentARB");
glad_glUniformHandleui64ARB = (PFNGLUNIFORMHANDLEUI64ARBPROC)load("glUniformHandleui64ARB");
glad_glUniformHandleui64vARB = (PFNGLUNIFORMHANDLEUI64VARBPROC)load("glUniformHandleui64vARB");
glad_glProgramUniformHandleui64ARB = (PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC)load("glProgramUniformHandleui64ARB");
glad_glProgramUniformHandleui64vARB = (PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC)load("glProgramUniformHandleui64vARB");
glad_glIsTextureHandleResidentARB = (PFNGLISTEXTUREHANDLERESIDENTARBPROC)load("glIsTextureHandleResidentARB");
glad_glIsImageHandleResidentARB = (PFNGLISIMAGEHANDLERESIDENTARBPROC)load("glIsImageHandleResidentARB");
glad_glVertexAttribL1ui64ARB = (PFNGLVERTEXATTRIBL1UI64ARBPROC)load("glVertexAttribL1ui64ARB");
glad_glVertexAttribL1ui64vARB = (PFNGLVERTEXATTRIBL1UI64VARBPROC)load("glVertexAttribL1ui64vARB");
glad_glGetVertexAttribLui64vARB = (PFNGLGETVERTEXATTRIBLUI64VARBPROC)load("glGetVertexAttribLui64vARB");
}
static int find_extensionsGL(void) {
if (!get_exts()) return 0;
(void)&has_ext;
GLAD_GL_ARB_bindless_texture = has_ext("GL_ARB_bindless_texture");
GLAD_GL_ARB_texture_filter_anisotropic = has_ext("GL_ARB_texture_filter_anisotropic");
free_exts();
return 1;
}
@ -2239,6 +2278,7 @@ int gladLoadGLLoader(GLADloadproc load) {
load_GL_VERSION_4_3(load);
if (!find_extensionsGL()) return 0;
load_GL_ARB_bindless_texture(load);
return GLVersion.major != 0 || GLVersion.minor != 0;
}