mirror of
https://github.com/open-goal/jak-project
synced 2026-08-16 21:28:00 -04:00
fonts and stuff work
This commit is contained in:
@@ -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());
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<u8, 4>& rgba,
|
||||
const math::Vector<u32, 3>& vert) {
|
||||
const math::Vector<u32, 3>& vert,
|
||||
const math::Vector<float, 2>& st) {
|
||||
rgba_u8[vert_count] = rgba;
|
||||
verts[vert_count] = vert;
|
||||
sts[vert_count] = st;
|
||||
vert_count++;
|
||||
}
|
||||
@@ -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<u8, 4> rgba_reg = {0, 0, 0, 0};
|
||||
math::Vector<float, 2> st_reg;
|
||||
|
||||
std::array<math::Vector<u8, 4>, 3> building_rgba;
|
||||
std::array<math::Vector<u32, 3>, 3> building_vert;
|
||||
std::array<math::Vector<float, 2>, 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<math::Vector<u8, 4>> rgba_u8;
|
||||
std::vector<math::Vector<u32, 3>> verts;
|
||||
std::vector<math::Vector<float, 2>> 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<u8, 4>& rgba, const math::Vector<u32, 3>& vert);
|
||||
void push(const math::Vector<u8, 4>& rgba,
|
||||
const math::Vector<u32, 3>& vert,
|
||||
const math::Vector<float, 2>& 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;
|
||||
};
|
||||
|
||||
@@ -38,8 +38,8 @@ void OpenGLRenderer::init_bucket_renderers() {
|
||||
init_bucket_renderer<EmptyBucketRenderer>("bucket0", BucketId::BUCKET0);
|
||||
|
||||
// TODO what the heck is drawing to debug-draw-0 on init?
|
||||
init_bucket_renderer<DirectRenderer>("debug-draw-0", BucketId::DEBUG_DRAW_0, 1024);
|
||||
init_bucket_renderer<DirectRenderer>("debug-draw-1", BucketId::DEBUG_DRAW_1, 1024);
|
||||
init_bucket_renderer<DirectRenderer>("debug-draw-0", BucketId::DEBUG_DRAW_0, 1024 * 8);
|
||||
init_bucket_renderer<DirectRenderer>("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++) {
|
||||
|
||||
@@ -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"};
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<TextureRecord>&& 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);
|
||||
}
|
||||
}
|
||||
@@ -13,15 +13,31 @@ struct TextureRecord {
|
||||
u16 w, h;
|
||||
std::vector<u8> 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<TextureRecord>&& 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<std::unique_ptr<TextureRecord>, 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<std::unique_ptr<TextureRecord>> m_garbage_textures;
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -445,6 +445,10 @@ void pc_texture_upload_now(u32 page, u32 mode) {
|
||||
Gfx::texture_upload_now(Ptr<u8>(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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user