From 0dfe6a169d85461361b8e6883c64fdec953e56f3 Mon Sep 17 00:00:00 2001 From: water Date: Sun, 8 Aug 2021 20:46:14 -0400 Subject: [PATCH] fonts and stuff work --- game/graphics/dma/gs.cpp | 15 ++ game/graphics/dma/gs.h | 63 ++++++ game/graphics/gfx.cpp | 6 + game/graphics/gfx.h | 1 + .../opengl_renderer/DirectRenderer.cpp | 183 +++++++++++++++--- .../graphics/opengl_renderer/DirectRenderer.h | 26 ++- .../opengl_renderer/OpenGLRenderer.cpp | 4 +- game/graphics/opengl_renderer/Shader.cpp | 1 + game/graphics/opengl_renderer/Shader.h | 3 +- .../shaders/direct_basic_textured.frag | 12 ++ .../shaders/direct_basic_textured.vert | 14 ++ game/graphics/texture/TexturePool.cpp | 33 +++- game/graphics/texture/TexturePool.h | 16 ++ game/kernel/kboot.cpp | 5 +- game/kernel/kmachine.cpp | 5 + goal_src/engine/gfx/texture.gc | 5 + goal_src/kernel-defs.gc | 1 + 17 files changed, 353 insertions(+), 40 deletions(-) create mode 100644 game/graphics/opengl_renderer/shaders/direct_basic_textured.frag create mode 100644 game/graphics/opengl_renderer/shaders/direct_basic_textured.vert diff --git a/game/graphics/dma/gs.cpp b/game/graphics/dma/gs.cpp index 039f4ca60d..1411bccd93 100644 --- a/game/graphics/dma/gs.cpp +++ b/game/graphics/dma/gs.cpp @@ -333,4 +333,19 @@ std::string GsAlpha::print() const { } return result; +} + +std::string GsTex1::print() const { + return fmt::format("lcm: {} mxl: {} mmag: {} mmin: {} mtba: {} l: {} k: {}\n", lcm(), mxl(), + mmag(), mmin(), mtba(), l(), k()); +} + +std::string GsTexa::print() const { + return fmt::format("ta0: {} aem: {} ta1: {}\n", ta0(), aem(), ta1()); +} + +std::string GsTex0::print() const { + return fmt::format( + "tbp0: {} tbw: {} psm: {} tw: {} th: {} tcc: {} tfx: {} cbp: {} cpsm: {} csm: {}\n", tbp0(), + tbw(), psm(), tw(), th(), tcc(), tfx(), cbp(), cpsm(), csm()); } \ No newline at end of file diff --git a/game/graphics/dma/gs.h b/game/graphics/dma/gs.h index 508a85462e..343176f168 100644 --- a/game/graphics/dma/gs.h +++ b/game/graphics/dma/gs.h @@ -242,4 +242,67 @@ struct GsPrim { bool operator==(const GsPrim& other) const { return data == other.data; } bool operator!=(const GsPrim& other) const { return data != other.data; } +}; + +struct GsTex0 { + GsTex0() = default; + GsTex0(u64 val) : data(val) {} + + u32 tbp0() const { return data & 0b11'1111'1111'1111; } + u32 tbw() const { return (data >> 14) & 0b111111; } + u32 psm() const { return (data >> 20) & 0b111111; } + u32 tw() const { return (data >> 26) & 0b1111; } + u32 th() const { return (data >> 30) & 0b1111; } + enum class TextureFunction : u8 { + MODULATE = 0, + DECAL = 1, + HIGHLIGHT = 2, + HIGHLIGHT2 = 3, + }; + u32 tcc() const { return ((data >> 34) & 0b1); } + TextureFunction tfx() const { return (TextureFunction)((data >> 35) & 0b11); } + u32 cbp() const { return (data >> 37) & 0b11'1111'1111'1111; } + u32 cpsm() const { return (data >> 51) & 0b1111; } + u32 csm() const { return (data >> 55) & 1; } + + std::string print() const; + + bool operator==(const GsTex0& other) const { return data == other.data; } + bool operator!=(const GsTex0& other) const { return data != other.data; } + + u64 data = 0; +}; + +struct GsTex1 { // tex1_1 and tex1_2 + GsTex1() = default; + GsTex1(u64 val) : data(val) {} + + bool lcm() const { + // 1 = fixed + return data & 1; + } + + u32 mxl() const { return (data >> 2) & 0b111; } + bool mmag() const { return data & (1 << 5); } + u32 mmin() const { return (data >> 6) & 0b111; } + bool mtba() const { return data & (1 << 9); } + u32 l() const { return (data >> 19) & 0b11; } + u32 k() const { return (data >> 32) & 0b1111'1111'1111; } + + std::string print() const; + + u64 data = 0; +}; + +struct GsTexa { + GsTexa() = default; + GsTexa(u64 val) : data(val) {} + + u8 ta0() const { return data; } + bool aem() const { return data & (1 << 15); } + u8 ta1() const { return (data >> 32); } + + std::string print() const; + + u64 data = 0; }; \ No newline at end of file diff --git a/game/graphics/gfx.cpp b/game/graphics/gfx.cpp index fcbc2b3f99..f63310ea19 100644 --- a/game/graphics/gfx.cpp +++ b/game/graphics/gfx.cpp @@ -202,4 +202,10 @@ void texture_upload_now(const u8* tpage, int mode, u32 s7_ptr) { } } +void texture_relocate(u32 destination, u32 source) { + if (g_gfx_data) { + g_gfx_data->texture_pool->relocate(destination, source); + } +} + } // namespace Gfx diff --git a/game/graphics/gfx.h b/game/graphics/gfx.h index 7130724b59..dc303db0e9 100644 --- a/game/graphics/gfx.h +++ b/game/graphics/gfx.h @@ -22,6 +22,7 @@ u32 Exit(); u32 vsync(); void send_chain(const void* data, u32 offset); void texture_upload_now(const u8* tpage, int mode, u32 s7_ptr); +void texture_relocate(u32 destination, u32 source); } // namespace Gfx diff --git a/game/graphics/opengl_renderer/DirectRenderer.cpp b/game/graphics/opengl_renderer/DirectRenderer.cpp index 5148dd79f0..b3b2413c4a 100644 --- a/game/graphics/opengl_renderer/DirectRenderer.cpp +++ b/game/graphics/opengl_renderer/DirectRenderer.cpp @@ -16,12 +16,17 @@ DirectRenderer::DirectRenderer(const std::string& name, BucketId my_id, int batc glBindBuffer(GL_ARRAY_BUFFER, m_ogl.color_buffer); m_ogl.color_buffer_bytes = batch_size * 3 * 4 * sizeof(u8); glBufferData(GL_ARRAY_BUFFER, m_ogl.color_buffer_bytes, nullptr, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, m_ogl.st_buffer); + m_ogl.st_buffer_bytes = batch_size * 3 * 2 * sizeof(float); + glBufferData(GL_ARRAY_BUFFER, m_ogl.st_buffer_bytes, nullptr, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } DirectRenderer::~DirectRenderer() { glDeleteBuffers(1, &m_ogl.color_buffer); glDeleteBuffers(1, &m_ogl.vertex_buffer); + glDeleteBuffers(1, &m_ogl.st_buffer); } /*! @@ -41,13 +46,14 @@ void DirectRenderer::render(DmaFollower& dma, SharedRenderState* render_state) { } if (dma.current_tag_offset() == render_state->default_regs_buffer) { - reset_state(); + // reset_state(); dma.read_and_advance(); // cnt assert(dma.current_tag().kind == DmaTag::Kind::RET); dma.read_and_advance(); // ret } } + fmt::print("update from end {}\n", m_prim_gl_state.texture_enable); flush_pending(render_state); } @@ -59,7 +65,7 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state) { lg::warn("DirectRenderer flush with {} triangles.\n", m_prim_buffer.vert_count / 3); // update opengl state if (m_prim_gl_state_needs_gl_update) { - update_gl_prim(); + update_gl_prim(render_state); m_prim_gl_state_needs_gl_update = false; } @@ -83,6 +89,13 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state) { glBufferSubData(GL_ARRAY_BUFFER, 0, m_ogl.vertex_buffer_bytes, m_prim_buffer.verts.data()); glBindBuffer(GL_ARRAY_BUFFER, m_ogl.color_buffer); glBufferSubData(GL_ARRAY_BUFFER, 0, m_ogl.color_buffer_bytes, m_prim_buffer.rgba_u8.data()); + if (m_prim_gl_state.texture_enable) { + glBindBuffer(GL_ARRAY_BUFFER, m_ogl.st_buffer); + glBufferSubData(GL_ARRAY_BUFFER, 0, m_ogl.st_buffer_bytes, m_prim_buffer.sts.data()); + for (int i = 0; i < 10; i++) { + fmt::print("{}: {} {}\n", i, m_prim_buffer.sts[i].x(), m_prim_buffer.sts[i].y()); + } + } // setup attributes: @@ -104,16 +117,32 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state) { GL_TRUE, // normalized, ignored, 0, // tightly packed 0); + + if (m_prim_gl_state.texture_enable) { + fmt::print("DO TEXTURE!\n"); + glBindBuffer(GL_ARRAY_BUFFER, m_ogl.st_buffer); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, // location 0 in the shader + 2, // 3 floats per vert + GL_FLOAT, // floats + GL_FALSE, // normalized, ignored, + 0, // tightly packed + 0); + glActiveTexture(GL_TEXTURE0); + } // assert(false); glDrawArrays(GL_TRIANGLES, 0, m_prim_buffer.vert_count); m_prim_buffer.vert_count = 0; } -void DirectRenderer::update_gl_prim() { +void DirectRenderer::update_gl_prim(SharedRenderState* render_state) { // currently gouraud is handled in setup. const auto& state = m_prim_gl_state; if (state.texture_enable) { - // assert(false); TODO + render_state->shaders[ShaderId::DIRECT_BASIC_TEXTURED].activate(); + update_gl_texture(render_state); + } else { + render_state->shaders[ShaderId::DIRECT_BASIC].activate(); } if (state.fogging_enable) { assert(false); @@ -132,6 +161,38 @@ void DirectRenderer::update_gl_prim() { } } +void DirectRenderer::upload_texture(TextureRecord* tex) { + assert(!tex->on_gpu); + GLuint tex_id; + glGenTextures(1, &tex_id); + tex->gpu_texture = tex_id; + glBindTexture(GL_TEXTURE_2D, tex_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->w, tex->h, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, + tex->data.data()); + glBindTexture(GL_TEXTURE_2D, 0); + tex->on_gpu = true; +} + +void DirectRenderer::update_gl_texture(SharedRenderState* render_state) { + auto tex = render_state->texture_pool->lookup(m_texture_state.texture_base_ptr); + assert(tex); + fmt::print("Successful texture lookup! {} {}\n", tex->page_name, tex->name); + + // first: do we need to load the texture? + if (!tex->on_gpu) { + upload_texture(tex); + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex->gpu_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glUniform1i( + glGetUniformLocation(render_state->shaders[ShaderId::DIRECT_BASIC_TEXTURED].id(), "T0"), 0); +} + void DirectRenderer::update_gl_blend() { const auto& state = m_blend_state; if (state.a == GsAlpha::BlendMode::SOURCE && state.b == GsAlpha::BlendMode::DEST && @@ -181,7 +242,6 @@ void DirectRenderer::update_gl_test() { void DirectRenderer::setup_common_state(SharedRenderState* render_state) { // todo texture clamp. - render_state->shaders[ShaderId::DIRECT_BASIC].activate(); } namespace { @@ -255,7 +315,6 @@ void DirectRenderer::render_vif(u32 vif0, * Render GIF data. */ void DirectRenderer::render_gif(const u8* data, u32 size, SharedRenderState* render_state) { - fmt::print("Render GIF: {}\n", size); assert(size >= 16); bool eop = false; @@ -263,7 +322,7 @@ void DirectRenderer::render_gif(const u8* data, u32 size, SharedRenderState* ren while (!eop) { GifTag tag(data + offset); offset += 16; - fmt::print("Tag: {}\n", tag.print()); + // fmt::print("Tag: {}\n", tag.print()); // unpack registers. // faster to do it once outside of the nloop loop. @@ -306,7 +365,7 @@ void DirectRenderer::render_gif(const u8* data, u32 size, SharedRenderState* ren for (u32 reg = 0; reg < nreg; reg++) { u64 register_data; memcpy(®ister_data, data + offset, 8); - fmt::print("loop: {} reg: {} {}\n", loop, reg, reg_descriptor_name(reg_desc[reg])); +// 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); @@ -334,7 +393,7 @@ void DirectRenderer::render_gif(const u8* data, u32 size, SharedRenderState* ren assert(offset == size); - fmt::print("{}\n", GifTag(data).print()); +// fmt::print("{}\n", GifTag(data).print()); } void DirectRenderer::handle_ad(const u8* data, SharedRenderState* render_state) { @@ -363,13 +422,23 @@ void DirectRenderer::handle_ad(const u8* data, SharedRenderState* render_state) handle_prim(value, render_state); break; - // for now, ignore textures/fog. - // TODO 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, render_state); break; default: fmt::print("Address {} is not supported\n", register_address_name(addr)); @@ -377,8 +446,60 @@ void DirectRenderer::handle_ad(const u8* data, SharedRenderState* render_state) } } +void DirectRenderer::handle_tex1_1(u64 val) { + GsTex1 reg(val); + // for now, we aren't going to handle mipmapping. I don't think it's used with direct. + assert(reg.mxl() == 0); + // if that's true, we can ignore LCM, MTBA, L, K + + // MMAG/MMIN specify texture filtering. For now, assume always linear + assert(reg.mmag() == true); + assert(reg.mmin() == 1); + + // fmt::print("{}\n", reg.print()); +} + +void DirectRenderer::handle_tex0_1(u64 val, SharedRenderState* render_state) { + GsTex0 reg(val); +// fmt::print("{}\n", reg.print()); + + // update tbp + if (m_texture_state.current_register != reg) { + flush_pending(render_state); + m_texture_state.texture_base_ptr = reg.tbp0(); + m_texture_state_needs_gl_update = true; + m_texture_state.current_register = reg; + } + + // tbw: assume they got it right + // psm: assume they got it right + // tw: assume they got it right + // th: assume they got it right + + // these mean that the texture is multiplied, and uses the alpha from the clut. + assert(reg.tcc() == 1); + assert(reg.tfx() == GsTex0::TextureFunction::MODULATE); + + // cbp: assume they got it right + // cpsm: assume they got it right + // csm: assume they got it right +} + +void DirectRenderer::handle_texa(u64 val) { + GsTexa reg(val); + + // rgba16 isn't used so this doesn't matter? + // but they use sane defaults anyway + assert(reg.ta0() == 0); + assert(reg.ta1() == 0x80); + + assert(reg.aem() == false); +} + void DirectRenderer::handle_st_packed(const u8* data) { - // TODO + memcpy(&m_prim_building.st_reg.x(), data + 0, 4); + memcpy(&m_prim_building.st_reg.y(), data + 4, 4); + memcpy(&m_prim_building.Q, data + 8, 4); } void DirectRenderer::handle_rgbaq_packed(const u8* data) { @@ -406,9 +527,6 @@ void DirectRenderer::handle_xyzf2_packed(const u8* data) { bool adc = upper & (1ull << 47); assert(!adc); assert(!f); - // TODO (not this) - fmt::print("font vert at {} {}\n", u32_to_float(x), u32_to_float(y)); - handle_xyzf2_common(x, y, z, f); } @@ -427,7 +545,6 @@ void DirectRenderer::handle_zbuf1(u64 val) { void DirectRenderer::handle_test1(u64 val, SharedRenderState* render_state) { GsTest reg(val); - fmt::print("test: {}\n", reg.print()); if (m_test_state.current_register != reg) { flush_pending(render_state); m_test_state.from_register(reg); @@ -437,7 +554,6 @@ void DirectRenderer::handle_test1(u64 val, SharedRenderState* render_state) { void DirectRenderer::handle_alpha1(u64 val, SharedRenderState* render_state) { GsAlpha reg(val); - fmt::print("alpha: {}\n", reg.print()); if (m_blend_state.current_register != reg) { flush_pending(render_state); m_blend_state.from_register(reg); @@ -454,8 +570,6 @@ void DirectRenderer::handle_clamp1(u64 val) { } void DirectRenderer::handle_prim(u64 val, SharedRenderState* render_state) { - fmt::print("got prim: 0x{:x}\n", val); - if (m_prim_building.tri_strip_startup) { m_prim_building.tri_strip_startup = 0; m_prim_building.building_idx = 0; @@ -481,11 +595,11 @@ void DirectRenderer::handle_prim(u64 val, SharedRenderState* render_state) { void DirectRenderer::handle_rgbaq(u64 val) { assert((val >> 32) == 0); // q = 0 memcpy(m_prim_building.rgba_reg.data(), &val, 4); - fmt::print("a = 0x{:x}\n", m_prim_building.rgba_reg[3]); } void DirectRenderer::handle_xyzf2_common(u32 x, u32 y, u32 z, u8 f) { assert(f == 0); + m_prim_building.building_st.at(m_prim_building.building_idx) = m_prim_building.st_reg; m_prim_building.building_rgba.at(m_prim_building.building_idx) = m_prim_building.rgba_reg; m_prim_building.building_vert.at(m_prim_building.building_idx) = {x << 16, y << 16, z}; m_prim_building.building_idx++; @@ -509,12 +623,12 @@ void DirectRenderer::handle_xyzf2_common(u32 x, u32 y, u32 z, u8 f) { auto& corner3_rgba = corner2_rgba; auto& corner4_rgba = corner2_rgba; - m_prim_buffer.push(corner1_rgba, corner1_vert); - m_prim_buffer.push(corner3_rgba, corner3_vert); - m_prim_buffer.push(corner2_rgba, corner2_vert); - m_prim_buffer.push(corner2_rgba, corner2_vert); - m_prim_buffer.push(corner4_rgba, corner4_vert); - m_prim_buffer.push(corner1_rgba, corner1_vert); + m_prim_buffer.push(corner1_rgba, corner1_vert, {}); + m_prim_buffer.push(corner3_rgba, corner3_vert, {}); + m_prim_buffer.push(corner2_rgba, corner2_vert, {}); + m_prim_buffer.push(corner2_rgba, corner2_vert, {}); + m_prim_buffer.push(corner4_rgba, corner4_vert, {}); + m_prim_buffer.push(corner1_rgba, corner1_vert, {}); m_prim_building.building_idx = 0; } } break; @@ -527,9 +641,10 @@ void DirectRenderer::handle_xyzf2_common(u32 x, u32 y, u32 z, u8 f) { m_prim_building.tri_strip_startup++; } if (m_prim_building.tri_strip_startup >= 3) { - m_prim_buffer.push(m_prim_building.building_rgba[0], m_prim_building.building_vert[0]); - m_prim_buffer.push(m_prim_building.building_rgba[1], m_prim_building.building_vert[1]); - m_prim_buffer.push(m_prim_building.building_rgba[2], m_prim_building.building_vert[2]); + for (int i = 0; i < 3; i++) { + m_prim_buffer.push(m_prim_building.building_rgba[i], m_prim_building.building_vert[i], + m_prim_building.building_st[i]); + } } } break; @@ -541,6 +656,7 @@ void DirectRenderer::handle_xyzf2_common(u32 x, u32 y, u32 z, u8 f) { void DirectRenderer::handle_xyzf2(u64 val, SharedRenderState* render_state) { if (m_prim_buffer.is_full()) { + fmt::print("update from full\n"); flush_pending(render_state); } @@ -564,6 +680,9 @@ void DirectRenderer::reset_state() { m_prim_gl_state_needs_gl_update = true; m_prim_gl_state = PrimGlState(); + m_texture_state_needs_gl_update = true; + m_texture_state = TextureState(); + m_prim_building = PrimBuildState(); } @@ -600,6 +719,7 @@ void DirectRenderer::PrimGlState::from_register(GsPrim reg) { current_register = reg; gouraud_enable = reg.gouraud(); texture_enable = reg.tme(); + fmt::print("set tex en: {}\n", texture_enable); fogging_enable = reg.fge(); aa_enable = reg.aa1(); use_uv = reg.fst(); @@ -610,12 +730,15 @@ void DirectRenderer::PrimGlState::from_register(GsPrim reg) { DirectRenderer::PrimitiveBuffer::PrimitiveBuffer(int max_triangles) { rgba_u8.resize(max_triangles * 3); verts.resize(max_triangles * 3); + sts.resize(max_triangles * 3); max_verts = max_triangles * 3; } void DirectRenderer::PrimitiveBuffer::push(const math::Vector& rgba, - const math::Vector& vert) { + const math::Vector& vert, + const math::Vector& st) { rgba_u8[vert_count] = rgba; verts[vert_count] = vert; + sts[vert_count] = st; vert_count++; } \ No newline at end of file diff --git a/game/graphics/opengl_renderer/DirectRenderer.h b/game/graphics/opengl_renderer/DirectRenderer.h index 18f0f52e86..0069412fa0 100644 --- a/game/graphics/opengl_renderer/DirectRenderer.h +++ b/game/graphics/opengl_renderer/DirectRenderer.h @@ -60,12 +60,18 @@ class DirectRenderer : public BucketRenderer { void handle_st_packed(const u8* data); void handle_rgbaq_packed(const u8* data); void handle_xyzf2_packed(const u8* data); + void handle_tex0_1(u64 val, SharedRenderState* render_state); + void handle_tex1_1(u64 val); + void handle_texa(u64 val); void handle_xyzf2_common(u32 x, u32 y, u32 z, u8 f); - void update_gl_prim(); + void update_gl_prim(SharedRenderState* render_state); void update_gl_blend(); void update_gl_test(); + void update_gl_texture(SharedRenderState* render_state); + + void upload_texture(TextureRecord* tex); struct TestState { void from_register(GsTest reg); @@ -111,37 +117,51 @@ class DirectRenderer : public BucketRenderer { bool fix = false; // what does this even do? } m_prim_gl_state; + struct TextureState { + GsTex0 current_register; + u32 texture_base_ptr = 0; + } m_texture_state; + // state set through the prim/rgbaq register that doesn't require changing GL stuff struct PrimBuildState { GsPrim::Kind kind = GsPrim::Kind::PRIM_7; math::Vector rgba_reg = {0, 0, 0, 0}; + math::Vector st_reg; std::array, 3> building_rgba; std::array, 3> building_vert; + std::array, 3> building_st; int building_idx = 0; int tri_strip_startup = 0; + float Q = 0; + } m_prim_building; struct PrimitiveBuffer { PrimitiveBuffer(int max_triangles); std::vector> rgba_u8; std::vector> verts; + std::vector> sts; int vert_count = 0; int max_verts = 0; // leave 6 free on the end so we always have room to flush one last primitive. bool is_full() { return max_verts < (vert_count - 6); } - void push(const math::Vector& rgba, const math::Vector& vert); + void push(const math::Vector& rgba, + const math::Vector& vert, + const math::Vector& st); } m_prim_buffer; struct { - GLuint vertex_buffer, color_buffer; + GLuint vertex_buffer, color_buffer, st_buffer; u32 vertex_buffer_bytes = 0; u32 color_buffer_bytes = 0; + u32 st_buffer_bytes = 0; } m_ogl; bool m_prim_gl_state_needs_gl_update = true; bool m_test_state_needs_gl_update = true; bool m_blend_state_needs_gl_update = true; + bool m_texture_state_needs_gl_update = true; }; diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index cfe30ac81b..183ebb0a3c 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -38,8 +38,8 @@ void OpenGLRenderer::init_bucket_renderers() { init_bucket_renderer("bucket0", BucketId::BUCKET0); // TODO what the heck is drawing to debug-draw-0 on init? - init_bucket_renderer("debug-draw-0", BucketId::DEBUG_DRAW_0, 1024); - init_bucket_renderer("debug-draw-1", BucketId::DEBUG_DRAW_1, 1024); + init_bucket_renderer("debug-draw-0", BucketId::DEBUG_DRAW_0, 1024 * 8); + init_bucket_renderer("debug-draw-1", BucketId::DEBUG_DRAW_1, 1024 * 8); // for now, for any unset renderers, just set them to an EmptyBucketRenderer. for (size_t i = 0; i < m_bucket_renderers.size(); i++) { diff --git a/game/graphics/opengl_renderer/Shader.cpp b/game/graphics/opengl_renderer/Shader.cpp index 8fa5ad8a2f..67763d1f56 100644 --- a/game/graphics/opengl_renderer/Shader.cpp +++ b/game/graphics/opengl_renderer/Shader.cpp @@ -66,4 +66,5 @@ void Shader::activate() { ShaderLibrary::ShaderLibrary() { at(ShaderId::TEST_SHADER) = {"test_shader"}; at(ShaderId::DIRECT_BASIC) = {"direct_basic"}; + at(ShaderId::DIRECT_BASIC_TEXTURED) = {"direct_basic_textured"}; } \ No newline at end of file diff --git a/game/graphics/opengl_renderer/Shader.h b/game/graphics/opengl_renderer/Shader.h index 50cb4aa63e..200afa19a3 100644 --- a/game/graphics/opengl_renderer/Shader.h +++ b/game/graphics/opengl_renderer/Shader.h @@ -9,6 +9,7 @@ class Shader { Shader() = default; void activate(); bool okay() const { return m_is_okay; } + uint id() const { return m_program; } private: uint m_frag_shader = 0; @@ -18,7 +19,7 @@ class Shader { }; // note: update the constructor in Shader.cpp -enum class ShaderId { TEST_SHADER = 0, DIRECT_BASIC = 1, MAX_SHADERS }; +enum class ShaderId { TEST_SHADER = 0, DIRECT_BASIC = 1, DIRECT_BASIC_TEXTURED = 2, MAX_SHADERS }; class ShaderLibrary { public: diff --git a/game/graphics/opengl_renderer/shaders/direct_basic_textured.frag b/game/graphics/opengl_renderer/shaders/direct_basic_textured.frag new file mode 100644 index 0000000000..bc9395f950 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/direct_basic_textured.frag @@ -0,0 +1,12 @@ +#version 330 core + +out vec4 color; + +in vec4 fragment_color; +in vec2 tex_coord; +uniform sampler2D tex_T0; + +void main() { + vec4 T0 = texture(tex_T0, tex_coord); + color = fragment_color * T0 * 2.0; +} diff --git a/game/graphics/opengl_renderer/shaders/direct_basic_textured.vert b/game/graphics/opengl_renderer/shaders/direct_basic_textured.vert new file mode 100644 index 0000000000..e56f31a5eb --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/direct_basic_textured.vert @@ -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 vec2 tex_coord_in; + +out vec4 fragment_color; +out vec2 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 + 0.5); + tex_coord = tex_coord_in; +} \ No newline at end of file diff --git a/game/graphics/texture/TexturePool.cpp b/game/graphics/texture/TexturePool.cpp index bb7e38cac4..c7d03a4a31 100644 --- a/game/graphics/texture/TexturePool.cpp +++ b/game/graphics/texture/TexturePool.cpp @@ -4,6 +4,7 @@ #include "common/util/assert.h" #include "common/util/FileUtil.h" #include "common/util/Timer.h" +#include "common/log/log.h" //////////////////////////////// // Extraction of textures @@ -118,8 +119,9 @@ void TexturePool::handle_upload_now(const u8* tpage, int mode, const u8* memory_ m_tex_converter.upload(memory_base + texture_page.segment[0].block_data_ptr, texture_page.segment[0].dest, size); } else { - assert(false); - // return; + // no reason to skip this, other than + lg::error("TexturePool skipping upload now with mode {}.", mode); + return; } // loop over all texture in the tpage and download them. @@ -144,7 +146,7 @@ void TexturePool::handle_upload_now(const u8* tpage, int mode, const u8* memory_ m_tex_converter.download_rgba8888(texture_record->data.data(), tex.dest[mip_idx], tex.width[mip_idx], ww, hh, tex.psm, tex.clutpsm, tex.clut_dest, size_bytes); - m_textures.at(tex.dest[mip_idx]) = std::move(texture_record); + set_texture(tex.dest[mip_idx], std::move(texture_record)); // Debug output. if (dump_textures_to_file) { @@ -165,4 +167,29 @@ void TexturePool::handle_upload_now(const u8* tpage, int mode, const u8* memory_ } fmt::print("upload now took {:.2f} ms\n", timer.getMs()); +} + +/*! + * Store a texture in the pool. Location is specified like TBP. + */ +void TexturePool::set_texture(u32 location, std::unique_ptr&& record) { + if (m_textures.at(location)) { + m_garbage_textures.push_back(std::move(m_textures[location])); + } + m_textures[location] = std::move(record); +} + +/*! + * Move a texture. + */ +void TexturePool::relocate(u32 destination, u32 source) { + if (m_textures.at(source)) { + if (m_textures.at(destination)) { + return; // HACK + } + m_textures.at(destination) = std::move(m_textures.at(source)); + fmt::print("Relocated a texture: {}\n", m_textures[destination]->name); + } else { + assert(false); + } } \ No newline at end of file diff --git a/game/graphics/texture/TexturePool.h b/game/graphics/texture/TexturePool.h index f891f1ed42..b65091e50a 100644 --- a/game/graphics/texture/TexturePool.h +++ b/game/graphics/texture/TexturePool.h @@ -13,15 +13,31 @@ struct TextureRecord { u16 w, h; std::vector data; u8 data_segment; + u64 gpu_texture = 0; + bool on_gpu = false; }; class TexturePool { public: void handle_upload_now(const u8* tpage, int mode, const u8* memory_base, u32 s7_ptr); + void set_texture(u32 location, std::unique_ptr&& record); + TextureRecord* lookup(u32 location) { + if (m_textures.at(location)) { + return m_textures[location].get(); + } else { + return nullptr; + } + } + + void relocate(u32 destination, u32 source); private: TextureConverter m_tex_converter; // uses tex.dest[mip] indexing. (bytes / 256). Currently only sets the base of a texture. std::array, 1024 * 1024 * 4 / 256> m_textures; + + // textures that the game overwrote, but may be still allocated on the GPU. + // TODO: free these periodically. + std::vector> m_garbage_textures; }; diff --git a/game/kernel/kboot.cpp b/game/kernel/kboot.cpp index 3234a87775..eb793d1f76 100644 --- a/game/kernel/kboot.cpp +++ b/game/kernel/kboot.cpp @@ -181,7 +181,10 @@ void KernelCheckAndDispatch() { SendAck(); } - std::this_thread::sleep_for(std::chrono::microseconds(1000)); + if (time_ms < 4) { + std::this_thread::sleep_for(std::chrono::microseconds(1000)); + } + } } diff --git a/game/kernel/kmachine.cpp b/game/kernel/kmachine.cpp index 0c003469b0..db85513030 100644 --- a/game/kernel/kmachine.cpp +++ b/game/kernel/kmachine.cpp @@ -445,6 +445,10 @@ void pc_texture_upload_now(u32 page, u32 mode) { Gfx::texture_upload_now(Ptr(page).c(), mode, s7.offset); } +void pc_texture_relocate(u32 dst, u32 src) { + Gfx::texture_relocate(dst, src); +} + /*! * Open a file-stream. Name is a GOAL string. Mode is a GOAL symbol. Use 'read for readonly * and anything else for write only. @@ -605,6 +609,7 @@ void InitMachine_PCPort() { make_function_symbol_from_c("__mem-move", (void*)c_memmove); make_function_symbol_from_c("__send-gfx-dma-chain", (void*)send_gfx_dma_chain); make_function_symbol_from_c("__pc-texture-upload-now", (void*)pc_texture_upload_now); + make_function_symbol_from_c("__pc-texture-relocate", (void*)pc_texture_relocate); } /*! diff --git a/goal_src/engine/gfx/texture.gc b/goal_src/engine/gfx/texture.gc index 2c43407b4b..696ebbbf84 100644 --- a/goal_src/engine/gfx/texture.gc +++ b/goal_src/engine/gfx/texture.gc @@ -1807,6 +1807,11 @@ Will try to move the whole thing, including the clut, assuming you provide a destination for it. Note that this uses the format/width stuff properly, so it will be slower, but won't scramble your texture." + (#when PC_PORT + ;; as far as I know this is only used for fonts which have 1 mip level. + (__pc-texture-relocate (/ dest-loc 64) (-> tex dest 0)) + ) + ;; loop over mips (dotimes (mip-level (the-as int (-> tex num-mips))) ;; size of the current mip. diff --git a/goal_src/kernel-defs.gc b/goal_src/kernel-defs.gc index a76bb6ce97..b72d896520 100644 --- a/goal_src/kernel-defs.gc +++ b/goal_src/kernel-defs.gc @@ -183,6 +183,7 @@ (define-extern __mem-move (function pointer pointer uint none)) (define-extern __send-gfx-dma-chain (function object object none)) (define-extern __pc-texture-upload-now (function object object none)) +(define-extern __pc-texture-relocate (function object object none)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; ksound - InitSoundScheme