* Initial depth-cue implementation

* Oops

* Finish merge + fix issues with letterboxing

* Fix alpha blending

* Add some debug options + profiler stats

* More debug options + respect xyoffset GS register

* Clean up GOAL code

* Disable depth-cue by default

* typo

* very important typo

* depth-cue disable, but better
This commit is contained in:
Ethan Lafrenais 2022-07-19 18:34:30 -04:00 committed by GitHub
parent 6e77b1154d
commit bb323c2ebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 1131 additions and 245 deletions

View file

@ -359,6 +359,14 @@ std::string GsPrim::print() const {
return fmt::format("0x{:x}, kind {}\n", data, kind());
}
std::string GsFrame::print() const {
return fmt::format("fbp: {} fbw: {} psm: {} fbmsk: {:x}\n", fbp(), fbw(), psm(), fbmsk());
}
std::string GsXYOffset::print() const {
return fmt::format("ofx: {} ofy: {}\n", ofx(), ofy());
}
std::string DrawMode::to_string() const {
std::string result;
result += fmt::format(" depth-write: {}\n", get_depth_write_enable());
@ -449,4 +457,4 @@ std::string DrawMode::to_string() const {
}
result += fmt::format(" fog: {}\n decal: {}\n", get_fog_enable(), get_decal());
return result;
}
}

View file

@ -341,6 +341,52 @@ struct GsTexa {
u64 data = 0;
};
struct GsFrame {
enum class PSM {
PSMCT32 = 0,
PSMCT24 = 1,
PSMCT16 = 2,
PSMCT16S = 0b1010,
PSMT8 = 0b10011,
PSMT4 = 0b10100,
PSMT8H = 0b011011,
PSMT4HL = 0b100100,
PSMT4HH = 0b101100,
PSMZ32 = 0b110000,
PSMZ24 = 0b110001,
PSMZ16 = 0b110010,
PSMZ16S = 0b111010
};
GsFrame() = default;
GsFrame(u64 val) : data(val) {}
// Frame buffer base pointer (word address / 2048)
u32 fbp() const { return (data & 0b1'1111'1111); }
// Frame buffer width (pixels / 64)
u32 fbw() const { return ((data >> 16) & 0b11'1111); }
// Frame buffer pixel storage format
PSM psm() const { return (PSM)((data >> 24) & 0b11'1111); }
// Frame buffer drawing mask
u32 fbmsk() const { return ((data >> 32) & 0xFFFF'FFFF); }
std::string print() const;
u64 data = 0;
};
struct GsXYOffset {
GsXYOffset() = default;
GsXYOffset(u64 val) : data(val) {}
u32 ofx() const { return data & 0xFFFF; }
u32 ofy() const { return (data >> 32) & 0xFFFF; }
std::string print() const;
u64 data = 0;
};
// not including the giftag
struct AdGifData {
u64 tex0_data;

View file

@ -138,6 +138,7 @@ set(RUNTIME_SOURCE
graphics/opengl_renderer/Sprite3.cpp
graphics/opengl_renderer/SpriteRenderer.cpp
graphics/opengl_renderer/TextureUploadHandler.cpp
graphics/opengl_renderer/DepthCue.cpp
graphics/texture/jak1_tpage_dir.cpp
graphics/texture/TextureConverter.cpp
graphics/texture/TexturePool.cpp

View file

@ -0,0 +1,684 @@
#include "DepthCue.h"
#include "game/graphics/opengl_renderer/dma_helpers.h"
#include "third-party/fmt/core.h"
#include "third-party/imgui/imgui.h"
namespace {
// Converts fixed point (with 4 bits for decimal) to floating point.
float fixed_to_floating_point(int fixed) {
return fixed / 16.0f;
}
math::Vector2f fixed_to_floating_point(const math::Vector<s32, 2>& fixed_vec) {
return math::Vector2f(fixed_to_floating_point(fixed_vec.x()),
fixed_to_floating_point(fixed_vec.y()));
}
} // namespace
// Total number of loops depth-cue performs to draw to the framebuffer
constexpr int TOTAL_DRAW_SLICES = 16;
DepthCue::DepthCue(const std::string& name, BucketId my_id) : BucketRenderer(name, my_id) {
opengl_setup();
m_draw_slices.resize(TOTAL_DRAW_SLICES);
}
void DepthCue::opengl_setup() {
// Gen texture for sampling the framebuffer
glGenFramebuffers(1, &m_ogl.framebuffer_sample_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_ogl.framebuffer_sample_fbo);
glGenTextures(1, &m_ogl.framebuffer_sample_tex);
glBindTexture(GL_TEXTURE_2D, m_ogl.framebuffer_sample_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
m_ogl.framebuffer_sample_tex, 0);
ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Gen framebuffer for depth-cue-base-page
glGenFramebuffers(1, &m_ogl.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_ogl.fbo);
glGenTextures(1, &m_ogl.fbo_texture);
glBindTexture(GL_TEXTURE_2D, m_ogl.fbo_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ogl.fbo_texture, 0);
ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Gen vertex array for drawing to depth-cue-base-page
glGenVertexArrays(1, &m_ogl.depth_cue_page_vao);
glBindVertexArray(m_ogl.depth_cue_page_vao);
glGenBuffers(1, &m_ogl.depth_cue_page_vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.depth_cue_page_vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(SpriteVertex), nullptr, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, // location 0 in the shader
2, // 2 floats per vert
GL_FLOAT, // floats
GL_FALSE, // don't normalize, ignored
sizeof(SpriteVertex), //
(void*)offsetof(SpriteVertex, xy) // offset in array
);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, // location 1 in the shader
2, // 2 floats per vert
GL_FLOAT, // floats
GL_FALSE, // don't normalize, ignored
sizeof(SpriteVertex), //
(void*)offsetof(SpriteVertex, st) // offset in array
);
// Gen vertex array for drawing to on-screen framebuffer
glGenVertexArrays(1, &m_ogl.on_screen_vao);
glBindVertexArray(m_ogl.on_screen_vao);
glGenBuffers(1, &m_ogl.on_screen_vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.on_screen_vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(SpriteVertex), nullptr, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, // location 0 in the shader
2, // 2 floats per vert
GL_FLOAT, // floats
GL_FALSE, // don't normalize, ignored
sizeof(SpriteVertex), //
(void*)offsetof(SpriteVertex, xy) // offset in array
);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, // location 1 in the shader
2, // 2 floats per vert
GL_FLOAT, // floats
GL_FALSE, // don't normalize, ignored
sizeof(SpriteVertex), //
(void*)offsetof(SpriteVertex, st) // offset in array
);
// Done
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void DepthCue::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) {
// First thing should be a NEXT with two nops. this is a jump from buckets to depth-cue
auto data0 = dma.read_and_advance();
ASSERT(data0.vif1() == 0);
ASSERT(data0.vif0() == 0);
ASSERT(data0.size_bytes == 0);
if (dma.current_tag().kind == DmaTag::Kind::CALL) {
// depth-cue renderer didn't run, let's just get out of here.
for (int i = 0; i < 4; i++) {
dma.read_and_advance();
}
ASSERT(dma.current_tag_offset() == render_state->next_bucket);
return;
}
// Read DMA
{
auto prof_node = prof.make_scoped_child("dma");
read_dma(dma, render_state, prof_node);
}
if (!m_enabled) {
// Renderer disabled, stop early
return;
}
// Set up draw info
{
auto prof_node = prof.make_scoped_child("setup");
setup(render_state, prof_node);
}
// Draw
{
auto prof_node = prof.make_scoped_child("drawing");
draw(render_state, prof_node);
}
}
/*!
* Reads all depth-cue DMA packets.
*/
void DepthCue::read_dma(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& /*prof*/) {
// First should be general GS register setup
{
auto gs_setup = dma.read_and_advance();
ASSERT(gs_setup.size_bytes == sizeof(DepthCueGsSetup));
ASSERT(gs_setup.vifcode0().kind == VifCode::Kind::NOP);
ASSERT(gs_setup.vifcode1().kind == VifCode::Kind::DIRECT);
memcpy(&m_gs_setup, gs_setup.data, sizeof(DepthCueGsSetup));
ASSERT(m_gs_setup.gif_tag.nreg() == 6);
ASSERT(m_gs_setup.gif_tag.reg(0) == GifTag::RegisterDescriptor::AD);
ASSERT(m_gs_setup.test1.ztest() == GsTest::ZTest::ALWAYS);
ASSERT(m_gs_setup.zbuf1.zmsk() == true);
ASSERT(m_gs_setup.tex1.mmag() == true);
ASSERT(m_gs_setup.tex1.mmin() == 1);
ASSERT(m_gs_setup.miptbp1 == 0);
ASSERT(m_gs_setup.alpha1.b_mode() == GsAlpha::BlendMode::DEST);
ASSERT(m_gs_setup.alpha1.d_mode() == GsAlpha::BlendMode::DEST);
}
// Next is 64 DMAs to draw to the depth-cue-base-page and back to the on-screen framebuffer
// We'll group these by each slice of the framebuffer being drawn to
for (int i = 0; i < TOTAL_DRAW_SLICES; i++) {
// Each 'slice' should be:
// 1. GS setup for drawing from on-screen framebuffer to depth-cue-base-page
// 2. Draw to depth-cue-base-page
// 3. GS setup for drawing from depth-cue-base-page back to on-screen framebuffer
// 4. Draw to on-screen framebuffer
DrawSlice& slice = m_draw_slices.at(i);
// depth-cue-base-page setup
{
auto depth_cue_page_setup = dma.read_and_advance();
ASSERT(depth_cue_page_setup.size_bytes == sizeof(DepthCuePageGsSetup));
ASSERT(depth_cue_page_setup.vifcode0().kind == VifCode::Kind::NOP);
ASSERT(depth_cue_page_setup.vifcode1().kind == VifCode::Kind::DIRECT);
memcpy(&slice.depth_cue_page_setup, depth_cue_page_setup.data, sizeof(DepthCuePageGsSetup));
ASSERT(slice.depth_cue_page_setup.gif_tag.nreg() == 5);
ASSERT(slice.depth_cue_page_setup.gif_tag.reg(0) == GifTag::RegisterDescriptor::AD);
ASSERT(slice.depth_cue_page_setup.tex01.tcc() == 1);
ASSERT(slice.depth_cue_page_setup.test1.ztest() == GsTest::ZTest::ALWAYS);
ASSERT(slice.depth_cue_page_setup.alpha1.b_mode() == GsAlpha::BlendMode::SOURCE);
ASSERT(slice.depth_cue_page_setup.alpha1.d_mode() == GsAlpha::BlendMode::SOURCE);
}
// depth-cue-base-page draw
{
auto depth_cue_page_draw = dma.read_and_advance();
ASSERT(depth_cue_page_draw.size_bytes == sizeof(DepthCuePageDraw));
ASSERT(depth_cue_page_draw.vifcode0().kind == VifCode::Kind::NOP);
ASSERT(depth_cue_page_draw.vifcode1().kind == VifCode::Kind::DIRECT);
memcpy(&slice.depth_cue_page_draw, depth_cue_page_draw.data, sizeof(DepthCuePageDraw));
ASSERT(slice.depth_cue_page_draw.gif_tag.nloop() == 1);
ASSERT(slice.depth_cue_page_draw.gif_tag.pre() == true);
ASSERT(slice.depth_cue_page_draw.gif_tag.prim() == 6);
ASSERT(slice.depth_cue_page_draw.gif_tag.flg() == GifTag::Format::PACKED);
ASSERT(slice.depth_cue_page_draw.gif_tag.nreg() == 5);
ASSERT(slice.depth_cue_page_draw.gif_tag.reg(0) == GifTag::RegisterDescriptor::RGBAQ);
}
// on-screen setup
{
auto on_screen_setup = dma.read_and_advance();
ASSERT(on_screen_setup.size_bytes == sizeof(OnScreenGsSetup));
ASSERT(on_screen_setup.vifcode0().kind == VifCode::Kind::NOP);
ASSERT(on_screen_setup.vifcode1().kind == VifCode::Kind::DIRECT);
memcpy(&slice.on_screen_setup, on_screen_setup.data, sizeof(OnScreenGsSetup));
ASSERT(slice.on_screen_setup.gif_tag.nreg() == 5);
ASSERT(slice.on_screen_setup.gif_tag.reg(0) == GifTag::RegisterDescriptor::AD);
ASSERT(slice.on_screen_setup.tex01.tcc() == 0);
ASSERT(slice.on_screen_setup.texa.ta0() == 0x80);
ASSERT(slice.on_screen_setup.texa.ta1() == 0x80);
ASSERT(slice.on_screen_setup.alpha1.b_mode() == GsAlpha::BlendMode::DEST);
ASSERT(slice.on_screen_setup.alpha1.d_mode() == GsAlpha::BlendMode::DEST);
}
// on-screen draw
{
auto on_screen_draw = dma.read_and_advance();
ASSERT(on_screen_draw.size_bytes == sizeof(OnScreenDraw));
ASSERT(on_screen_draw.vifcode0().kind == VifCode::Kind::NOP);
ASSERT(on_screen_draw.vifcode1().kind == VifCode::Kind::DIRECT);
memcpy(&slice.on_screen_draw, on_screen_draw.data, sizeof(OnScreenDraw));
ASSERT(slice.on_screen_draw.gif_tag.nloop() == 1);
ASSERT(slice.on_screen_draw.gif_tag.pre() == true);
ASSERT(slice.on_screen_draw.gif_tag.prim() == 6);
ASSERT(slice.on_screen_draw.gif_tag.flg() == GifTag::Format::PACKED);
ASSERT(slice.on_screen_draw.gif_tag.nreg() == 5);
ASSERT(slice.on_screen_draw.gif_tag.reg(0) == GifTag::RegisterDescriptor::RGBAQ);
}
}
// Finally, a packet to restore GS state
{
auto gs_restore = dma.read_and_advance();
ASSERT(gs_restore.size_bytes == sizeof(DepthCueGsRestore));
ASSERT(gs_restore.vifcode0().kind == VifCode::Kind::NOP);
ASSERT(gs_restore.vifcode1().kind == VifCode::Kind::DIRECT);
memcpy(&m_gs_restore, gs_restore.data, sizeof(DepthCueGsRestore));
ASSERT(m_gs_restore.gif_tag.nreg() == 2);
ASSERT(m_gs_restore.gif_tag.reg(0) == GifTag::RegisterDescriptor::AD);
}
// End with 'NEXT'
{
ASSERT(dma.current_tag().kind == DmaTag::Kind::NEXT);
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
}
}
}
void DepthCue::setup(SharedRenderState* render_state, ScopedProfilerNode& /*prof*/) {
if (m_debug.cache_setup && (m_ogl.last_draw_region_w == render_state->draw_region_w &&
m_ogl.last_draw_region_h == render_state->draw_region_h &&
// Also recompute when certain debug settings change
m_ogl.last_override_sharpness == m_debug.override_sharpness &&
m_ogl.last_custom_sharpness == m_debug.sharpness &&
m_ogl.last_force_original_res == m_debug.force_original_res &&
m_ogl.last_res_scale == m_debug.res_scale)) {
// Draw region didn't change, everything is already set up
return;
}
m_ogl.last_draw_region_w = render_state->draw_region_w;
m_ogl.last_draw_region_h = render_state->draw_region_h;
m_ogl.last_override_sharpness = m_debug.override_sharpness;
m_ogl.last_custom_sharpness = m_debug.sharpness;
m_ogl.last_force_original_res = m_debug.force_original_res;
m_ogl.last_res_scale = m_debug.res_scale;
// ASSUMPTIONS
// --------------------------
// Assert some assumptions that most of the data for each depth-cue draw is the same.
// The way the game wants to render this effect is very inefficient in OpenGL, we can use these
// assumptions to only alter state once, do setup once, and group multiple draw calls.
const DrawSlice& first_slice = m_draw_slices[0];
// 1. Assume each draw slice has the exact same width of 32
// We'll compare each slice to the first as we go
float slice_width = fixed_to_floating_point(first_slice.on_screen_draw.xyzf2_2.x() -
first_slice.on_screen_draw.xyzf2_1.x());
// NOTE: Y-coords will range between [0,1/2 output res], usually 224 but not always.
// We'll capture it here so we can convert to coords to [0,1] ranges later.
float slice_height = fixed_to_floating_point(first_slice.on_screen_draw.xyzf2_2.y());
ASSERT(slice_width == 32.0f);
ASSERT(first_slice.on_screen_draw.xyzf2_1.y() == 0);
// 2. Assume that the framebuffer is sampled as a 1024x256 texel view and that the game thinks the
// framebuffer is 512 pixels wide.
int fb_sample_width = (int)pow(2, first_slice.depth_cue_page_setup.tex01.tw());
int fb_sample_height = (int)pow(2, first_slice.depth_cue_page_setup.tex01.th());
int fb_width = first_slice.depth_cue_page_setup.tex01.tbw() * 64;
ASSERT(fb_sample_width == 1024);
ASSERT(fb_sample_height == 256);
ASSERT(fb_width == 512);
ASSERT(fb_width * 2 == fb_sample_width);
// 3. Finally, assert that all slices match the above assumptions
for (const DrawSlice& slice : m_draw_slices) {
float _slice_width = fixed_to_floating_point(slice.on_screen_draw.xyzf2_2.x() -
slice.on_screen_draw.xyzf2_1.x());
float _slice_height = fixed_to_floating_point(slice.on_screen_draw.xyzf2_2.y());
ASSERT(slice_width == _slice_width);
ASSERT(slice_height == _slice_height);
ASSERT(slice.on_screen_draw.xyzf2_1.y() == 0);
int _fb_sample_width = (int)pow(2, slice.depth_cue_page_setup.tex01.tw());
int _fb_sample_height = (int)pow(2, slice.depth_cue_page_setup.tex01.th());
int _fb_width = slice.depth_cue_page_setup.tex01.tbw() * 64;
ASSERT(fb_sample_width == _fb_sample_width);
ASSERT(fb_sample_height == _fb_sample_height);
ASSERT(fb_width == _fb_width);
}
// FRAMEBUFFER SAMPLE TEXTURE
// --------------------------
// We need a copy of the framebuffer to sample from. If the framebuffer wasn't using
// multisampling, this wouldn't be necessary and the framebuffer could just be bound. Instead,
// we'll just blit to a new texture.
//
// The original game code would have created this as a view into the framebuffer whose width is 2x
// as large, however this isn't necessary for the effect to work.
int pc_fb_sample_width = render_state->draw_region_w;
int pc_fb_sample_height = render_state->draw_region_h;
m_ogl.framebuffer_sample_width = pc_fb_sample_width;
m_ogl.framebuffer_sample_height = pc_fb_sample_height;
glBindTexture(GL_TEXTURE_2D, m_ogl.framebuffer_sample_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, pc_fb_sample_width, pc_fb_sample_height, 0, GL_RGB,
GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// DEPTH CUE BASE PAGE FRAMEBUFFER
// --------------------------
// Next, we need a framebuffer to draw slices of the sample texture to. The depth-cue effect
// usually does this in 16 vertical slices that are 32 pixels wide each. The destination
// drawn to is smaller than the source by a very small amount (defined by sharpness in the
// GOAL code), which kicks in the bilinear filtering effect. Normally, a 32x224 texture will
// be reused for each slice but for the sake of efficient rendering, we'll create a framebuffer
// that can store all 16 slices side-by-side and draw all slices to it all at once.
int pc_depth_cue_fb_width = render_state->draw_region_w;
int pc_depth_cue_fb_height = render_state->draw_region_h;
if (m_debug.force_original_res) {
pc_depth_cue_fb_width = 512;
}
pc_depth_cue_fb_width *= m_debug.res_scale;
m_ogl.fbo_width = pc_depth_cue_fb_width;
m_ogl.fbo_height = pc_depth_cue_fb_height;
glBindTexture(GL_TEXTURE_2D, m_ogl.fbo_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, pc_depth_cue_fb_width, pc_depth_cue_fb_height, 0, GL_RGB,
GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// DEPTH CUE BASE PAGE VERTEX DATA
// --------------------------
// Now that we have a framebuffer to draw each slice to, we need the actual vertex data.
// We'll take the exact data DMA'd and scale it up the PC dimensions.
std::vector<SpriteVertex> depth_cue_page_vertices;
// U-coordinates here will range from [0,512], but the maximum U value in the original is
// 1024 since the sample texel width is usually 1024. Since we're not using a texture with
// 2x the width, the maximum U value used to convert UVs to [0,1] should be 512.
float max_u = fb_sample_width / 2.0f;
ASSERT(max_u == 512.0f);
for (const auto& slice : m_draw_slices) {
math::Vector2f xyoffset = fixed_to_floating_point(
math::Vector2<s32>((s32)slice.depth_cue_page_setup.xyoffset1.ofx(),
(s32)slice.depth_cue_page_setup.xyoffset1.ofy()));
math::Vector2f xy1 = fixed_to_floating_point(slice.depth_cue_page_draw.xyzf2_1.xy());
math::Vector2f xy2 = fixed_to_floating_point(slice.depth_cue_page_draw.xyzf2_2.xy());
math::Vector2f uv1 = fixed_to_floating_point(slice.depth_cue_page_draw.uv_1.xy());
math::Vector2f uv2 = fixed_to_floating_point(slice.depth_cue_page_draw.uv_2.xy());
ASSERT(xy1.x() == 0);
ASSERT(xy1.y() == 0);
ASSERT(xy2.x() <= 32.0f);
ASSERT(xy2.y() <= slice_height);
if (m_debug.override_sharpness) {
// Undo sharpness from GOAL code and apply custom
xy2.x() = 32.0f * m_debug.sharpness;
xy2.y() = 224.0f * m_debug.sharpness;
}
// Apply xyoffset GS register
xy1.x() += xyoffset.x() / 4096.0f;
xy1.y() += xyoffset.y() / 4096.0f;
xy2.x() += xyoffset.x() / 4096.0f;
xy2.y() += xyoffset.y() / 4096.0f;
// U-coord will range from [0,512], which is half of the original framebuffer sample width
// Let's also use it to determine the X offset into the depth-cue framebuffer since the
// original draw assumes each slice is at 0,0.
float x_offset = (uv1.x() / 512.0f) * (xy2.x() / 32.0f);
build_sprite(depth_cue_page_vertices,
// Top-left
(xy1.x() / 512.0f) + x_offset, // x1
xy1.y() / slice_height, // y1
uv1.x() / max_u, // s1
uv1.y() / slice_height, // t1
// Bottom-right
(xy2.x() / 512.0f) + x_offset, // x2
xy2.y() / slice_height, // y2
uv2.x() / max_u, // s2
uv2.y() / slice_height // t2
);
}
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.depth_cue_page_vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, depth_cue_page_vertices.size() * sizeof(SpriteVertex),
depth_cue_page_vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// ON SCREEN VERTEX DATA
// --------------------------
// Finally, we need to draw pixels from the depth-cue-base-page back to the on-screen
// framebuffer. We'll take the same approach as above.
std::vector<SpriteVertex> on_screen_vertices;
for (const auto& slice : m_draw_slices) {
math::Vector2f xyoffset = fixed_to_floating_point(math::Vector2<s32>(
(s32)slice.on_screen_setup.xyoffset1.ofx(), (s32)slice.on_screen_setup.xyoffset1.ofy()));
math::Vector2f xy1 = fixed_to_floating_point(slice.on_screen_draw.xyzf2_1.xy());
math::Vector2f xy2 = fixed_to_floating_point(slice.on_screen_draw.xyzf2_2.xy());
math::Vector2f uv1 = fixed_to_floating_point(slice.on_screen_draw.uv_1.xy());
math::Vector2f uv2 = fixed_to_floating_point(slice.on_screen_draw.uv_2.xy());
ASSERT(uv1.x() == 0);
ASSERT(uv1.y() == 0);
ASSERT(uv2.x() <= 32.0f);
ASSERT(uv2.y() <= slice_height);
if (m_debug.override_sharpness) {
// Undo sharpness from GOAL code and apply custom
uv2.x() = 32.0f * m_debug.sharpness;
uv2.y() = 224.0f * m_debug.sharpness;
}
// Apply xyoffset GS register
xy1.x() += xyoffset.x() / 4096.0f;
xy1.y() += xyoffset.y() / 4096.0f;
xy2.x() += xyoffset.x() / 4096.0f;
xy2.y() += xyoffset.y() / 4096.0f;
// X-coord will range from [0,512], which is half of the original framebuffer sample width
// Let's also use it to determine the U offset into the on-screen framebuffer since the
// original draw assumes each slice is at 0,0.
float u_offset = (xy1.x() / 512.0f) * (uv2.x() / 32.0f);
build_sprite(on_screen_vertices,
// Top-left
xy1.x() / 512.0f, // x1
xy1.y() / slice_height, // y1
(uv1.x() / 512.0f) + u_offset, // s1
uv1.y() / slice_height, // t1
// Bottom-right
xy2.x() / 512.0f, // x2
xy2.y() / slice_height, // y2
(uv2.x() / 512.0f) + u_offset, // s2
uv2.y() / slice_height // t2
);
}
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.on_screen_vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, on_screen_vertices.size() * sizeof(SpriteVertex),
on_screen_vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void DepthCue::draw(SharedRenderState* render_state, ScopedProfilerNode& prof) {
// Disable depth writing but keep test
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
// Activate shader
auto shader = &render_state->shaders[ShaderId::DEPTH_CUE];
shader->activate();
glUniform1i(glGetUniformLocation(shader->id(), "tex"), 0);
// First, we need to copy the framebuffer into the framebuffer sample texture
glBindFramebuffer(GL_READ_FRAMEBUFFER, render_state->render_fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_ogl.framebuffer_sample_fbo);
glBlitFramebuffer(render_state->draw_offset_x, // srcX0
render_state->draw_offset_y, // srcY0
render_state->draw_offset_x + render_state->draw_region_w, // srcX1
render_state->draw_offset_y + render_state->draw_region_h, // srcY1
0, // dstX0
0, // dstY0
m_ogl.framebuffer_sample_width, // dstX1
m_ogl.framebuffer_sample_height, // dstY1
GL_COLOR_BUFFER_BIT, // mask
GL_NEAREST // filter
);
glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb);
// Next, we need to draw from the framebuffer sample texture to the depth-cue-base-page
// framebuffer
{
const auto& depth_cue_page_draw = m_draw_slices[0].depth_cue_page_draw;
math::Vector4f colorf = math::Vector4f(
depth_cue_page_draw.rgbaq.x() / 255.0f, depth_cue_page_draw.rgbaq.y() / 255.0f,
depth_cue_page_draw.rgbaq.z() / 255.0f, depth_cue_page_draw.rgbaq.w() / 255.0f);
glUniform4fv(glGetUniformLocation(shader->id(), "u_color"), 1, colorf.data());
glUniform1f(glGetUniformLocation(shader->id(), "u_depth"), 1.0f);
glBindFramebuffer(GL_FRAMEBUFFER, m_ogl.fbo);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_ogl.framebuffer_sample_tex);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ZERO);
glViewport(0, 0, m_ogl.fbo_width, m_ogl.fbo_height);
prof.add_draw_call();
prof.add_tri(2 * TOTAL_DRAW_SLICES);
glBindVertexArray(m_ogl.depth_cue_page_vao);
glDrawArrays(GL_TRIANGLES, 0, 6 * TOTAL_DRAW_SLICES); // 6 verts per slice
}
// Finally, the contents of depth-cue-base-page need to be overlayed onto the on-screen
// framebuffer
{
const auto& on_screen_draw = m_draw_slices[0].on_screen_draw;
math::Vector4f colorf =
math::Vector4f(on_screen_draw.rgbaq.x() / 255.0f, on_screen_draw.rgbaq.y() / 255.0f,
on_screen_draw.rgbaq.z() / 255.0f, on_screen_draw.rgbaq.w() / 255.0f);
if (m_debug.override_alpha) {
colorf.w() = m_debug.draw_alpha / 2.0f;
}
glUniform4fv(glGetUniformLocation(shader->id(), "u_color"), 1, colorf.data());
if (m_debug.depth == 1.0f) {
glUniform1f(glGetUniformLocation(shader->id(), "u_depth"), m_debug.depth);
} else {
// Scale debug depth expontentially to make the slider easier to use
glUniform1f(glGetUniformLocation(shader->id(), "u_depth"), pow(m_debug.depth, 8));
}
glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_ogl.fbo_texture);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
glViewport(render_state->draw_offset_x, render_state->draw_offset_y,
render_state->draw_region_w, render_state->draw_region_h);
prof.add_draw_call();
prof.add_tri(2 * TOTAL_DRAW_SLICES);
glBindVertexArray(m_ogl.on_screen_vao);
glDrawArrays(GL_TRIANGLES, 0, 6 * TOTAL_DRAW_SLICES); // 6 verts per slice
}
// Done
glDepthMask(GL_TRUE);
glBindTexture(GL_TEXTURE_2D, 0);
glBindVertexArray(0);
}
void DepthCue::build_sprite(std::vector<SpriteVertex>& vertices,
float x1,
float y1,
float s1,
float t1,
float x2,
float y2,
float s2,
float t2) {
// First triangle
// -------------
// Top-left
vertices.push_back(SpriteVertex(x1, y1, s1, t1));
// Top-right
vertices.push_back(SpriteVertex(x2, y1, s2, t1));
// Bottom-left
vertices.push_back(SpriteVertex(x1, y2, s1, t2));
// Second triangle
// -------------
// Top-right
vertices.push_back(SpriteVertex(x2, y1, s2, t1));
// Bottom-left
vertices.push_back(SpriteVertex(x1, y2, s1, t2));
// Bottom-right
vertices.push_back(SpriteVertex(x2, y2, s2, t2));
}
void DepthCue::draw_debug_window() {
ImGui::Text("NOTE: depth-cue may be disabled by '*vu1-enable-user-menu*'!");
ImGui::Checkbox("Cache setup", &m_debug.cache_setup);
ImGui::Checkbox("Force original resolution", &m_debug.force_original_res);
ImGui::Checkbox("Override alpha", &m_debug.override_alpha);
if (m_debug.override_alpha) {
ImGui::SliderFloat("Alpha", &m_debug.draw_alpha, 0.0f, 1.0f);
}
ImGui::Checkbox("Override sharpness", &m_debug.override_sharpness);
if (m_debug.override_sharpness) {
ImGui::SliderFloat("Sharpness", &m_debug.sharpness, 0.001f, 1.0f);
}
ImGui::SliderFloat("Depth", &m_debug.depth, 0.0f, 1.0f);
ImGui::SliderFloat("Resolution scale", &m_debug.res_scale, 0.001f, 2.0f);
if (ImGui::Button("Reset")) {
m_debug.draw_alpha = 0.4f;
m_debug.sharpness = 0.999f;
m_debug.depth = 1.0f;
m_debug.res_scale = 1.0f;
}
}

View file

@ -0,0 +1,183 @@
#pragma once
#include "common/dma/gs.h"
#include "common/math/Vector.h"
#include "game/graphics/opengl_renderer/BucketRenderer.h"
class DepthCue : public BucketRenderer {
public:
DepthCue(const std::string& name, BucketId my_id);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
private:
// One-time initial GS setup per frame
struct DepthCueGsSetup {
GifTag gif_tag;
GsTest test1;
u64 test1_addr;
GsZbuf zbuf1;
u64 zbuf1_addr;
GsTex1 tex1;
u64 tex1_addr;
u64 miptbp1;
u64 miptbp1_addr;
u64 clamp1;
u64 clamp1_addr;
GsAlpha alpha1;
u64 alpha1_addr;
};
static_assert(sizeof(DepthCueGsSetup) == (7 * 16));
// One-time GS state restoration at the end of each depth-cue frame
struct DepthCueGsRestore {
GifTag gif_tag;
u64 xyoffset1;
u64 xyoffset1_addr;
GsFrame frame1;
u64 frame1_addr;
};
static_assert(sizeof(DepthCueGsRestore) == (3 * 16));
// GS setup for drawing to the depth-cue-base-page framebuffer
struct DepthCuePageGsSetup {
GifTag gif_tag;
GsXYOffset xyoffset1;
u64 xyoffset1_addr;
GsFrame frame1;
u64 frame1_addr;
GsTex0 tex01;
u64 tex01_addr;
GsTest test1;
u64 test1_addr;
GsAlpha alpha1;
u64 alpha1_addr;
};
static_assert(sizeof(DepthCuePageGsSetup) == (6 * 16));
// GS setup for drawing to the on-screen framebuffer
struct OnScreenGsSetup {
GifTag gif_tag;
GsXYOffset xyoffset1;
u64 xyoffset1_addr;
GsFrame frame1;
u64 frame1_addr;
GsTexa texa;
u64 texa_addr;
GsTex0 tex01;
u64 tex01_addr;
GsAlpha alpha1;
u64 alpha1_addr;
};
static_assert(sizeof(OnScreenGsSetup) == (6 * 16));
// Sprite draw command to the depth-cue-base-page framebuffer
struct DepthCuePageDraw {
GifTag gif_tag;
math::Vector4<s32> rgbaq;
math::Vector4<s32> uv_1;
math::Vector4<s32> xyzf2_1;
math::Vector4<s32> uv_2;
math::Vector4<s32> xyzf2_2;
};
static_assert(sizeof(DepthCuePageDraw) == (6 * 16));
// Sprite draw command to the on-screen framebuffer
struct OnScreenDraw {
GifTag gif_tag;
math::Vector4<s32> rgbaq;
math::Vector4<s32> uv_1;
math::Vector4<s32> xyzf2_1;
math::Vector4<s32> uv_2;
math::Vector4<s32> xyzf2_2;
};
static_assert(sizeof(OnScreenDraw) == (6 * 16));
// A draw to the depth-cue-base-page and then back to the on-screen framebuffer.
//
// This is done 16 times per frame across the entire on-screen framebuffer in vertical strips.
struct DrawSlice {
DepthCuePageGsSetup depth_cue_page_setup;
DepthCuePageDraw depth_cue_page_draw;
OnScreenGsSetup on_screen_setup;
OnScreenDraw on_screen_draw;
};
struct SpriteVertex {
math::Vector2f xy;
math::Vector2f st;
SpriteVertex() = default;
SpriteVertex(float x, float y, float s, float t) : xy(x, y), st(s, t) {}
};
DepthCueGsSetup m_gs_setup;
DepthCueGsRestore m_gs_restore;
std::vector<DrawSlice> m_draw_slices;
struct {
// Framebuffer for depth-cue-base-page
GLuint fbo;
GLuint fbo_texture;
int fbo_width = 0;
int fbo_height = 0;
// Vertex data for drawing to depth-cue-base-page
GLuint depth_cue_page_vao;
GLuint depth_cue_page_vertex_buffer;
// Vertex data for drawing to on-screen framebuffer
GLuint on_screen_vao;
GLuint on_screen_vertex_buffer;
// Texture to sample the framebuffer from
GLuint framebuffer_sample_fbo;
GLuint framebuffer_sample_tex;
int framebuffer_sample_width = 0;
int framebuffer_sample_height = 0;
int last_draw_region_w = -1;
int last_draw_region_h = -1;
bool last_override_sharpness = false;
float last_custom_sharpness = 0.999f;
bool last_force_original_res = false;
float last_res_scale = 1.0f;
} m_ogl;
struct {
// false = recompute setup each frame
// true = only recompute setup when draw dimensions change
bool cache_setup = true;
// true = render depth-cue at original 512px wide resolution
bool force_original_res = false;
// true = render with m_draw_alpha alpha
bool override_alpha = false;
// 0.4 = default in GOAL
float draw_alpha = 0.4f;
// true = render with m_sharpness sharpness
bool override_sharpness = false;
// 1.0 = pixel perfect, depth-cue has no effect
// 0.999 = default in GOAL
float sharpness = 0.999f;
// lower to have effect only apply to further away pixels
// 1.0 = default (apply to all)
float depth = 1.0f;
// depth-cue resolution multiplier
float res_scale = 1.0f;
} m_debug;
void opengl_setup();
void read_dma(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof);
void setup(SharedRenderState* render_state, ScopedProfilerNode& prof);
void draw(SharedRenderState* render_state, ScopedProfilerNode& prof);
void build_sprite(std::vector<SpriteVertex>& vertices,
float x1,
float y1,
float x2,
float y2,
float s1,
float t1,
float s2,
float t2);
};

View file

@ -3,6 +3,7 @@
#include "common/log/log.h"
#include "common/util/FileUtil.h"
#include "game/graphics/opengl_renderer/DepthCue.h"
#include "game/graphics/opengl_renderer/DirectRenderer.h"
#include "game/graphics/opengl_renderer/EyeRenderer.h"
#include "game/graphics/opengl_renderer/ShadowRenderer.h"
@ -256,7 +257,11 @@ void OpenGLRenderer::init_bucket_renderers() {
BucketId::GENERIC_WATER_LEVEL1); // 62
init_bucket_renderer<OceanNear>("ocean-near", BucketCategory::OCEAN, BucketId::OCEAN_NEAR); // 63
// 64?
//-----------------------
// DEPTH CUE
//-----------------------
init_bucket_renderer<DepthCue>("depth-cue", BucketCategory::OTHER, BucketId::DEPTH_CUE); // 64
//-----------------------
// COMMON texture

View file

@ -90,6 +90,7 @@ ShaderLibrary::ShaderLibrary() {
at(ShaderId::SPRITE_DISTORT) = {"sprite_distort"};
at(ShaderId::SPRITE_DISTORT_INSTANCED) = {"sprite_distort_instanced"};
at(ShaderId::POST_PROCESSING) = {"post_processing"};
at(ShaderId::DEPTH_CUE) = {"depth_cue"};
for (auto& shader : m_shaders) {
ASSERT_MSG(shader.okay(), "Shader compiled");

View file

@ -45,6 +45,7 @@ enum class ShaderId {
SPRITE_DISTORT = 20,
SPRITE_DISTORT_INSTANCED = 21,
POST_PROCESSING = 22,
DEPTH_CUE = 23,
MAX_SHADERS
};

View file

@ -67,7 +67,7 @@ enum class BucketId {
MERC_WATER_LEVEL1 = 61,
GENERIC_WATER_LEVEL1 = 62,
OCEAN_NEAR = 63,
// 64
DEPTH_CUE = 64,
PRE_SPRITE_TEX = 65, // maybe it's just common textures?
SPRITE = 66,
DEBUG = 67,

View file

@ -0,0 +1,13 @@
#version 430 core
in flat vec4 fragment_color;
in vec2 tex_coord;
uniform sampler2D tex;
out vec4 out_color;
void main() {
// sample texture
out_color = fragment_color * texture(tex, tex_coord);
}

View file

@ -0,0 +1,27 @@
#version 430 core
layout (location = 0) in vec2 xy;
layout (location = 1) in vec2 st;
uniform vec4 u_color;
uniform float u_depth;
out flat vec4 fragment_color;
out vec2 tex_coord;
void main() {
// Calculate color
vec4 color = u_color;
color *= 2; // correct
fragment_color = color;
// Pass on texture coord
tex_coord = st;
// Calculate vertex position
vec4 position = vec4(xy.x, xy.y, u_depth, 1.0);
position.xyz = (position.xyz * 2) - 1.0; // convert from [0,1] to clip-space
gl_Position = position;
}

View file

@ -714,6 +714,10 @@
)
;; depth cue
(if (not *progress-process*)
(depth-cue disp)
)
;; screen filter
(with-profiler "post-sync-draw"

View file

@ -14,14 +14,42 @@
:dma (new 'static 'dma-tag :qwc #x6 :id (dma-tag-id cnt))
:vif1 (new 'static 'vif-tag :imm #x6 :cmd (vif-cmd direct) :msk #x1)
)
:gif (new 'static 'array uint64 2 #x50ab400000008001 #x43431)
:gif0 (new 'static 'gif-tag64
:nloop 1
:eop 1
:pre 1
:prim (gs-prim-type sprite) ;; actually #b001101010110
:flg (gif-flag packed)
:nreg 5
)
:gif1 (new 'static 'gif-tag-regs
:regs0 (gif-reg-id rgbaq)
:regs1 (gif-reg-id uv)
:regs2 (gif-reg-id xyzf2)
:regs3 (gif-reg-id uv)
:regs4 (gif-reg-id xyzf2)
)
)
:temp-strip-tmpl (new 'static 'dma-gif-packet
:dma-vif (new 'static 'dma-packet
:dma (new 'static 'dma-tag :qwc #x6 :id (dma-tag-id cnt))
:vif1 (new 'static 'vif-tag :imm #x6 :cmd (vif-cmd direct) :msk #x1)
)
:gif (new 'static 'array uint64 2 #x508b400000008001 #x43431)
:gif0 (new 'static 'gif-tag64
:nloop 1
:eop 1
:pre 1
:prim (gs-prim-type sprite) ;; actually #b00100010110
:flg (gif-flag packed)
:nreg 5
)
:gif1 (new 'static 'gif-tag-regs
:regs0 (gif-reg-id rgbaq)
:regs1 (gif-reg-id uv)
:regs2 (gif-reg-id xyzf2)
:regs3 (gif-reg-id uv)
:regs4 (gif-reg-id xyzf2)
)
)
:stencil-tmpl (new 'static 'dma-gif-packet
:dma-vif (new 'static 'dma-packet
@ -255,145 +283,93 @@
#f
)
(defun depth-cue-draw-front ((arg0 dma-buffer) (arg1 int) (arg2 float) (arg3 float) (arg4 uint) (arg5 int))
(set! (-> *depth-cue-work* draw-color w) (the int (* 128.0 arg3)))
(let ((v1-1 (the int (* 512.0 arg2)))
(a2-1 (the int (* (the float (* (-> *video-parms* screen-sy) 16)) arg2)))
(a3-4 0)
(t2-0 512)
(t3-0 *depth-cue-base-page*)
(defun depth-cue-draw-front ((dma-buff dma-buffer) (depth int) (sharpness float) (alpha float) (on-screen-fbp uint) (oddeven int))
;; set draw-color alpha
(set! (-> *depth-cue-work* draw-color w) (the int (* 128.0 alpha)))
;; sample the framebuffer in 16 vertical strips shrinking each a little,
;; then overlay them back onto the framebuffer
(let ((strip-w (the int (* 512.0 sharpness)))
(strip-h (the int (* (the float (* (-> *video-parms* screen-sy) 16)) sharpness)))
(x-offset 0) ;; incremented by 512 each loop
(strip-max-w 512)
)
(dotimes (t4-0 16)
(let* ((t5-0 arg0)
(t6-0 (the-as object (-> t5-0 base)))
)
(set! (-> (the-as dma-packet t6-0) dma) (new 'static 'dma-tag :qwc #x6 :id (dma-tag-id cnt)))
(set! (-> (the-as dma-packet t6-0) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet t6-0) vif1) (new 'static 'vif-tag :imm #x6 :cmd (vif-cmd direct) :msk #x1))
(set! (-> t5-0 base) (the-as pointer (the-as gs-gif-tag (&+ (the-as pointer t6-0) 16))))
;; repeat for each strip
(dotimes (i 16)
;; sample strip from framebuffer to depth-cue-base-page
;; ------------------------------------------------------
;; update GS registers
(dma-buffer-add-gs-set dma-buff
;; set vertex position offset
(xyoffset-1 (new 'static 'gs-xy-offset :ofy (- 8 oddeven)))
;; draw to depth-cue-base-page (64px wide)
(frame-1 (new 'static 'gs-frame :fbw #x1 :fbp *depth-cue-base-page*))
;; use on-screen framebuffer as texture 0
;; buffer width = 512 pixels
;; USIZE = 1024 texels
;; VSIZE = 256 texels
;; RGBA
(tex0-1 (new 'static 'gs-tex0 :tbw #x8 :tw #xa :th #x8 :tcc #x1 :tbp0 (* on-screen-fbp 32)))
;; enable depth test, all pixels pass
(test-1 (new 'static 'gs-test :zte #x1 :ztst (gs-ztest always)))
;; keep source rgb, no blending
(alpha-1 (new 'static 'gs-alpha))
)
(let* ((t5-1 arg0)
(t6-2 (the-as object (-> t5-1 base)))
)
(set! (-> (the-as gs-gif-tag t6-2) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x5))
(set! (-> (the-as gs-gif-tag t6-2) regs)
(new 'static 'gif-tag-regs
:regs0 (gif-reg-id a+d)
:regs1 (gif-reg-id a+d)
:regs2 (gif-reg-id a+d)
:regs3 (gif-reg-id a+d)
:regs4 (gif-reg-id a+d)
:regs5 (gif-reg-id a+d)
:regs6 (gif-reg-id a+d)
:regs7 (gif-reg-id a+d)
:regs8 (gif-reg-id a+d)
:regs9 (gif-reg-id a+d)
:regs10 (gif-reg-id a+d)
:regs11 (gif-reg-id a+d)
:regs12 (gif-reg-id a+d)
:regs13 (gif-reg-id a+d)
:regs14 (gif-reg-id a+d)
:regs15 (gif-reg-id a+d)
)
)
(set! (-> t5-1 base) (&+ (the-as pointer t6-2) 16))
;; draw sprite
(let ((t5-3 (the-as (inline-array vector4w) (-> dma-buff base))))
(set! (-> t5-3 0 quad) (-> *depth-cue-work* temp-strip-tmpl dma-vif quad)) ;; cnt direct
(set! (-> t5-3 1 quad) (-> *depth-cue-work* temp-strip-tmpl quad 1)) ;; draw sprite, 5 regs
(set! (-> t5-3 2 quad) (-> *depth-cue-work* set-color quad)) ;; rgbaq
(set-vector! (-> t5-3 3) x-offset 0 0 0) ;; uv
(set-vector! (-> t5-3 4) 0 0 0 0) ;; xyzf2
(set-vector! (-> t5-3 5) (+ x-offset strip-max-w) (* (-> *video-parms* screen-sy) 16) 0 0) ;; uv
(set-vector! (-> t5-3 6) strip-w strip-h 0 #x10000) ;; xyzf2
)
(let* ((t5-2 arg0)
(t6-4 (-> t5-2 base))
)
(set! (-> (the-as (pointer gs-xy-offset) t6-4) 0) (new 'static 'gs-xy-offset :ofy (- 8 arg5)))
(set! (-> (the-as (pointer gs-reg64) t6-4) 1) (gs-reg64 xyoffset-1))
(set! (-> (the-as (pointer gs-frame) t6-4) 2) (new 'static 'gs-frame :fbw #x1 :fbp t3-0))
(set! (-> (the-as (pointer gs-reg64) t6-4) 3) (gs-reg64 frame-1))
(set! (-> (the-as (pointer gs-tex0) t6-4) 4)
(new 'static 'gs-tex0 :tbw #x8 :tw #xa :th #x8 :tcc #x1 :tbp0 (* arg4 32))
)
(set! (-> (the-as (pointer gs-reg64) t6-4) 5) (gs-reg64 tex0-1))
(set! (-> (the-as (pointer gs-test) t6-4) 6) (new 'static 'gs-test :zte #x1 :ztst (gs-ztest always)))
(set! (-> (the-as (pointer gs-reg64) t6-4) 7) (gs-reg64 test-1))
(set! (-> (the-as (pointer gs-alpha) t6-4) 8) (new 'static 'gs-alpha))
(set! (-> (the-as (pointer gs-reg64) t6-4) 9) (gs-reg64 alpha-1))
(set! (-> t5-2 base) (&+ t6-4 80))
(&+! (-> dma-buff base) 112)
;; overlay strip back onto the on-screen framebuffer
;; ------------------------------------------------------
;; update GS registers
(dma-buffer-add-gs-set dma-buff
;; set vertex position offset
(xyoffset-1 (new 'static 'gs-xy-offset :ofy (+ oddeven 4)))
;; draw to on-screen-fbp but only the alpha channel
(frame-1 (new 'static 'gs-frame :fbw #x8 :fbmsk #xff000000 :fbp on-screen-fbp))
;; use an alpha value of 128
(texa (new 'static 'gs-texa :ta0 #x80 :ta1 #x80))
;; use depth-cue-base-page as texture 0
;; buffer width = 64 pixels
;; format = PSMCT24
;; USIZE = 32 texels
;; VSIZE = 256 texels
;; RGB
(tex0-1 (new 'static 'gs-tex0 :tbw #x1 :psm #x1 :tw #x5 :th #x8 :tbp0 (* *depth-cue-base-page* 32)))
;; blend source and framebuffer RGB
(alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1))
)
(let ((t5-3 (the-as (inline-array vector4w) (-> arg0 base))))
(set! (-> t5-3 0 quad) (-> *depth-cue-work* temp-strip-tmpl dma-vif quad))
(set! (-> t5-3 1 quad) (-> *depth-cue-work* temp-strip-tmpl quad 1))
(set! (-> t5-3 2 quad) (-> *depth-cue-work* set-color quad))
(set-vector! (-> t5-3 3) a3-4 0 0 0)
(set-vector! (-> t5-3 4) 0 0 0 0)
(set-vector! (-> t5-3 5) (+ a3-4 t2-0) (* (-> *video-parms* screen-sy) 16) 0 0)
(set-vector! (-> t5-3 6) v1-1 a2-1 0 #x10000)
)
(set! (-> arg0 base) (the-as pointer (-> (the-as depth-cue-work (-> arg0 base)) set-color)))
(let* ((t5-7 arg0)
(t6-16 (the-as object (-> t5-7 base)))
)
(set! (-> (the-as dma-packet t6-16) dma) (new 'static 'dma-tag :qwc #x6 :id (dma-tag-id cnt)))
(set! (-> (the-as dma-packet t6-16) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet t6-16) vif1) (new 'static 'vif-tag :imm #x6 :cmd (vif-cmd direct) :msk #x1))
(set! (-> t5-7 base) (the-as pointer (the-as gs-gif-tag (&+ (the-as pointer t6-16) 16))))
)
(let* ((t5-8 arg0)
(t6-18 (the-as object (-> t5-8 base)))
)
(set! (-> (the-as gs-gif-tag t6-18) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x5))
(set! (-> (the-as gs-gif-tag t6-18) regs) (new 'static 'gif-tag-regs
:regs0 (gif-reg-id a+d)
:regs1 (gif-reg-id a+d)
:regs2 (gif-reg-id a+d)
:regs3 (gif-reg-id a+d)
:regs4 (gif-reg-id a+d)
:regs5 (gif-reg-id a+d)
:regs6 (gif-reg-id a+d)
:regs7 (gif-reg-id a+d)
:regs8 (gif-reg-id a+d)
:regs9 (gif-reg-id a+d)
:regs10 (gif-reg-id a+d)
:regs11 (gif-reg-id a+d)
:regs12 (gif-reg-id a+d)
:regs13 (gif-reg-id a+d)
:regs14 (gif-reg-id a+d)
:regs15 (gif-reg-id a+d)
)
)
(set! (-> t5-8 base) (&+ (the-as pointer t6-18) 16))
)
(let* ((t5-9 arg0)
(t6-20 (-> t5-9 base))
)
(set! (-> (the-as (pointer gs-xy-offset) t6-20) 0) (new 'static 'gs-xy-offset :ofy (+ arg5 4)))
(set! (-> (the-as (pointer gs-reg64) t6-20) 1) (gs-reg64 xyoffset-1))
(set! (-> (the-as (pointer gs-frame) t6-20) 2) (new 'static 'gs-frame :fbw #x8 :fbmsk #xff000000 :fbp arg4))
(set! (-> (the-as (pointer gs-reg64) t6-20) 3) (gs-reg64 frame-1))
(set! (-> (the-as (pointer gs-texa) t6-20) 4) (new 'static 'gs-texa :ta0 #x80 :ta1 #x80))
(set! (-> (the-as (pointer gs-reg64) t6-20) 5) (gs-reg64 texa))
(set! (-> (the-as (pointer gs-tex0) t6-20) 6)
(new 'static 'gs-tex0 :tbw #x1 :psm #x1 :tw #x5 :th #x8 :tbp0 (* t3-0 32))
)
(set! (-> (the-as (pointer gs-reg64) t6-20) 7) (gs-reg64 tex0-1))
(set! (-> (the-as (pointer gs-alpha) t6-20) 8) (new 'static 'gs-alpha :b #x1 :d #x1))
(set! (-> (the-as (pointer gs-reg64) t6-20) 9) (gs-reg64 alpha-1))
(set! (-> t5-9 base) (&+ t6-20 80))
)
(let ((t5-10 (the-as object (-> arg0 base))))
;; draw sprite
(let ((t5-10 (the-as object (-> dma-buff base))))
(set! (-> (the-as (inline-array vector4w) t5-10) 0 quad)
(-> *depth-cue-work* texture-strip-tmpl dma-vif quad)
)
(set! (-> (the-as (inline-array vector4w) t5-10) 1 quad) (-> *depth-cue-work* texture-strip-tmpl quad 1))
(set! (-> (the-as (inline-array vector4w) t5-10) 2 quad) (-> *depth-cue-work* draw-color quad))
(set-vector! (-> (the-as (inline-array vector4w) t5-10) 3) 0 0 0 0)
(set-vector! (-> (the-as (inline-array vector4w) t5-10) 4) a3-4 0 0 0)
(set-vector! (-> (the-as (inline-array vector4w) t5-10) 5) v1-1 a2-1 0 0)
(set-vector!
(set! (-> (the-as (inline-array vector4w) t5-10) 2 quad) (-> *depth-cue-work* draw-color quad)) ;; rgbaq
(set-vector! (-> (the-as (inline-array vector4w) t5-10) 3) 0 0 0 0) ;; uv
(set-vector! (-> (the-as (inline-array vector4w) t5-10) 4) x-offset 0 0 0) ;; xyzf2
(set-vector! (-> (the-as (inline-array vector4w) t5-10) 5) strip-w strip-h 0 0) ;; uv
(set-vector! ;; xyzf2
(the-as vector4w (&+ (the-as pointer t5-10) 96))
(+ a3-4 t2-0)
(+ x-offset strip-max-w)
(* (-> *video-parms* screen-sy) 16)
arg1
depth
#x10000
)
)
(set! (-> arg0 base) (the-as pointer (-> (the-as depth-cue-work (-> arg0 base)) set-color)))
(+! a3-4 t2-0)
(&+! (-> dma-buff base) 112)
;; next strip
;; ------------------------------------------------------
(+! x-offset strip-max-w)
)
)
#f
@ -407,132 +383,63 @@
)
)
(defun depth-cue ((arg0 display))
(defun depth-cue ((disp display))
;; is depth-cue enabled?
(when (logtest? (vu1-renderer-mask depth-cue) *vu1-enable-user*)
(let* ((gp-0 (-> *display* frames (-> *display* on-screen) frame global-buf base))
(s4-0 (-> *display* frames (-> *display* on-screen) frame global-buf))
(s5-0 (-> s4-0 base))
(let* ((dma-start (-> *display* frames (-> *display* on-screen) frame global-buf base))
(dma-buff (-> *display* frames (-> *display* on-screen) frame global-buf))
(s5-0 (-> dma-buff base))
)
(let ((v1-9 (-> arg0 on-screen)))
(let* ((a1-7 s4-0)
(a2-0 (the-as object (-> a1-7 base)))
)
(set! (-> (the-as dma-packet a2-0) dma) (new 'static 'dma-tag :qwc #x7 :id (dma-tag-id cnt)))
(set! (-> (the-as dma-packet a2-0) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet a2-0) vif1) (new 'static 'vif-tag :imm #x7 :cmd (vif-cmd direct) :msk #x1))
(set! (-> a1-7 base) (&+ (the-as pointer a2-0) 16))
)
(let* ((a1-8 s4-0)
(a2-2 (the-as object (-> a1-8 base)))
)
(set! (-> (the-as gs-gif-tag a2-2) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x6))
(set! (-> (the-as gs-gif-tag a2-2) regs) (new 'static 'gif-tag-regs
:regs0 (gif-reg-id a+d)
:regs1 (gif-reg-id a+d)
:regs2 (gif-reg-id a+d)
:regs3 (gif-reg-id a+d)
:regs4 (gif-reg-id a+d)
:regs5 (gif-reg-id a+d)
:regs6 (gif-reg-id a+d)
:regs7 (gif-reg-id a+d)
:regs8 (gif-reg-id a+d)
:regs9 (gif-reg-id a+d)
:regs10 (gif-reg-id a+d)
:regs11 (gif-reg-id a+d)
:regs12 (gif-reg-id a+d)
:regs13 (gif-reg-id a+d)
:regs14 (gif-reg-id a+d)
:regs15 (gif-reg-id a+d)
)
)
(set! (-> a1-8 base) (&+ (the-as pointer a2-2) 16))
)
(let* ((a1-9 s4-0)
(a2-4 (-> a1-9 base))
)
(set! (-> (the-as (pointer gs-test) a2-4) 0) (new 'static 'gs-test :zte #x1 :ztst (gs-ztest always)))
(set! (-> (the-as (pointer gs-reg64) a2-4) 1) (gs-reg64 test-1))
(set! (-> (the-as (pointer gs-zbuf) a2-4) 2) (new 'static 'gs-zbuf :zbp #x1c0 :psm (gs-psm ct24) :zmsk #x1))
(set! (-> (the-as (pointer gs-reg64) a2-4) 3) (gs-reg64 zbuf-1))
(set! (-> (the-as (pointer gs-reg64) a2-4) 4) (gs-reg64 signal))
(set! (-> (the-as (pointer gs-reg64) a2-4) 5) (gs-reg64 tex1-1))
(set! (-> (the-as (pointer gs-tex1) a2-4) 6) (new 'static 'gs-tex1))
(set! (-> (the-as (pointer gs-reg64) a2-4) 7) (gs-reg64 miptbp1-1))
(set! (-> (the-as (pointer gs-clamp) a2-4) 8) (new 'static 'gs-clamp
:wms (gs-tex-wrap-mode clamp)
:wmt (gs-tex-wrap-mode clamp)
:maxu #x1ff
:maxv (-> *video-parms* screen-masky)
)
)
(set! (-> (the-as (pointer gs-reg64) a2-4) 9) (gs-reg64 clamp-1))
(set! (-> (the-as (pointer gs-reg64) a2-4) 10) (gs-reg64 dimx))
(set! (-> (the-as (pointer gs-reg64) a2-4) 11) (gs-reg64 alpha-1))
(set! (-> a1-9 base) (&+ a2-4 96))
(let ((v1-9 (-> disp on-screen)))
;; initial GS register setup
(dma-buffer-add-gs-set dma-buff
;; depth test on, all pixels pass
(test-1 (new 'static 'gs-test :zte #x1 :ztst (gs-ztest always)))
;; don't write to depth buffer
(zbuf-1 (new 'static 'gs-zbuf :zbp #x1c0 :psm (gs-psm ct24) :zmsk #x1))
;; bilinear texture sampling
(tex1-1 (new 'static 'gs-tex1 :mmag 1 :mmin 1))
;; no mip
(miptbp1-1 (new 'static 'gs-miptbp))
;; set texture wrap mode to clamp
(clamp-1 (new 'static 'gs-clamp
:wms (gs-tex-wrap-mode clamp)
:wmt (gs-tex-wrap-mode clamp)
:maxu #x1ff
:maxv (-> *video-parms* screen-masky)
))
;; blend source and dest
(alpha-1 (new 'static 'gs-alpha :b 1 :d 1))
)
(let ((s3-0 (* *oddeven* 8))
(s2-0 (-> arg0 frames v1-9 draw frame1 fbp))
(on-screen-fbp (-> disp frames v1-9 draw frame1 fbp))
)
;; draw
(depth-cue-draw-front
s4-0
#xffffff
(-> *depth-cue-work* front data x)
(-> *depth-cue-work* front data y)
s2-0
s3-0
dma-buff ;; dma-buff
#xffffff ;; depth
(-> *depth-cue-work* front data x) ;; sharpness
(-> *depth-cue-work* front data y) ;; alpha
on-screen-fbp ;; on-screen frame pointer
s3-0 ;; oddeven * 8
)
(let* ((v1-19 s4-0)
(a0-2 (the-as object (-> v1-19 base)))
)
(set! (-> (the-as dma-packet a0-2) dma) (new 'static 'dma-tag :qwc #x3 :id (dma-tag-id cnt)))
(set! (-> (the-as dma-packet a0-2) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet a0-2) vif1) (new 'static 'vif-tag :imm #x3 :cmd (vif-cmd direct) :msk #x1))
(set! (-> v1-19 base) (&+ (the-as pointer a0-2) 16))
)
(let* ((v1-20 s4-0)
(a0-4 (the-as object (-> v1-20 base)))
)
(set! (-> (the-as gs-gif-tag a0-4) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x2))
(set! (-> (the-as gs-gif-tag a0-4) regs) (new 'static 'gif-tag-regs
:regs0 (gif-reg-id a+d)
:regs1 (gif-reg-id a+d)
:regs2 (gif-reg-id a+d)
:regs3 (gif-reg-id a+d)
:regs4 (gif-reg-id a+d)
:regs5 (gif-reg-id a+d)
:regs6 (gif-reg-id a+d)
:regs7 (gif-reg-id a+d)
:regs8 (gif-reg-id a+d)
:regs9 (gif-reg-id a+d)
:regs10 (gif-reg-id a+d)
:regs11 (gif-reg-id a+d)
:regs12 (gif-reg-id a+d)
:regs13 (gif-reg-id a+d)
:regs14 (gif-reg-id a+d)
:regs15 (gif-reg-id a+d)
)
)
(set! (-> v1-20 base) (&+ (the-as pointer a0-4) 16))
)
(let* ((v1-21 s4-0)
(a0-6 (-> v1-21 base))
)
(set! (-> (the-as (pointer gs-xy-offset) a0-6) 0)
(new 'static 'gs-xy-offset :ofx #x7000 :ofy (+ (* (-> *video-parms* screen-miny) 16) s3-0))
)
(set! (-> (the-as (pointer gs-reg64) a0-6) 1) (gs-reg64 xyoffset-1))
(set! (-> (the-as (pointer gs-frame) a0-6) 2) (new 'static 'gs-frame :fbw #x8 :fbp s2-0))
(set! (-> (the-as (pointer gs-reg64) a0-6) 3) (gs-reg64 frame-1))
(set! (-> v1-21 base) (&+ a0-6 32))
;; restore GS registers
(dma-buffer-add-gs-set dma-buff
;; restore xyoffset register
(xyoffset-1 (new 'static 'gs-xy-offset :ofx #x7000 :ofy (+ (* (-> *video-parms* screen-miny) 16) s3-0)))
;; restore drawing destination to on-screen framebuffer
(frame-1 (new 'static 'gs-frame :fbw #x8 :fbp on-screen-fbp))
)
)
)
(let ((a3-18 (-> s4-0 base)))
(let ((v1-22 (the-as object (-> s4-0 base))))
(let ((a3-18 (-> dma-buff base)))
;; add DMA chain
(let ((v1-22 (the-as object (-> dma-buff base))))
(set! (-> (the-as dma-packet v1-22) dma) (new 'static 'dma-tag :id (dma-tag-id next)))
(set! (-> (the-as dma-packet v1-22) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet v1-22) vif1) (new 'static 'vif-tag))
(set! (-> s4-0 base) (&+ (the-as pointer v1-22) 16))
(set! (-> dma-buff base) (&+ (the-as pointer v1-22) 16))
)
(let ((v0-0 (dma-bucket-insert-tag
(-> *display* frames (-> *display* on-screen) frame bucket-group)
@ -542,13 +449,14 @@
)
)
)
;; record memory usage
(let ((v1-27 *dma-mem-usage*))
(when (nonzero? v1-27)
(set! (-> v1-27 length) (max 84 (-> v1-27 length)))
(set! (-> v1-27 data 83 name) "depth-cue")
(+! (-> v1-27 data 83 count) 1)
(+! (-> v1-27 data 83 used)
(&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint gp-0))
(&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint dma-start))
)
(set! (-> v1-27 data 83 total) (-> v1-27 data 83 used))
)

View file

@ -54,6 +54,11 @@
)
)
;; disable depth-cue by default for the PC port
(#when PC_PORT
(logclear! *vu1-enable-user-menu* (vu1-renderer-mask depth-cue))
)
;; by default, all off.
;; the menu renderers get copied to this on each frame
(define *vu1-enable-user* (the-as vu1-renderer-mask 0))