diff --git a/include/dolphin/gx/GXAurora.h b/include/dolphin/gx/GXAurora.h index 72e584b..ff5f52c 100644 --- a/include/dolphin/gx/GXAurora.h +++ b/include/dolphin/gx/GXAurora.h @@ -61,6 +61,18 @@ extern "C" { #define GX_AURORA_DESTROY_COPY_TEX 0x0034 +#define GX_AURORA_LOAD_COPY_SRC 0x0035 + +#define GX_AURORA_LOAD_COPY_DST 0x0036 + +#define GX_AURORA_LOAD_COPY_DEST 0x0037 + +#define GX_AURORA_REQUEST_DEPTH_SNAPSHOT 0x0038 + +#define GX_AURORA_BEGIN_OFFSCREEN 0x0039 + +#define GX_AURORA_END_OFFSCREEN 0x003A + /** * Draw primitives with the vertex count derived from a byte length, as written by * GXBegin(prim, fmt, GX_AUTO). Must be followed by a u8 draw opcode (vtxfmt|prim), diff --git a/include/dolphin/gx/GXDispList.h b/include/dolphin/gx/GXDispList.h index 41597c1..b9a664d 100644 --- a/include/dolphin/gx/GXDispList.h +++ b/include/dolphin/gx/GXDispList.h @@ -7,15 +7,8 @@ extern "C" { #endif -#ifdef AURORA -#define GXCallDisplayListNative GXCallDisplayListLE -#else -#define GXCallDisplayListNative GXCallDisplayList -#endif - void GXBeginDisplayList(void* list, u32 size); u32 GXEndDisplayList(void); -void GXCallDisplayListLE(const void* list, u32 nbytes); void GXCallDisplayList(const void* list, u32 nbytes); #ifdef __cplusplus diff --git a/lib/dolphin/gx/GXAurora.cpp b/lib/dolphin/gx/GXAurora.cpp index 1485f0f..2549a6b 100644 --- a/lib/dolphin/gx/GXAurora.cpp +++ b/lib/dolphin/gx/GXAurora.cpp @@ -76,11 +76,11 @@ void GX2SetPolygonOffset(f32 mFrontOffset, f32 mFrontScale, f32 mBackOffset, f32 } void GXCreateFrameBuffer(u32 width, u32 height) { - aurora::gx::fifo::drain(); - aurora::gfx::begin_offscreen(width, height); + GX_WRITE_AURORA(GX_AURORA_BEGIN_OFFSCREEN); + GX_WRITE_U32(width); + GX_WRITE_U32(height); } void GXRestoreFrameBuffer() { - aurora::gx::fifo::drain(); - aurora::gfx::end_offscreen(); + GX_WRITE_AURORA(GX_AURORA_END_OFFSCREEN); } diff --git a/lib/dolphin/gx/GXCpu2Efb.cpp b/lib/dolphin/gx/GXCpu2Efb.cpp index 2128662..9f3f87d 100644 --- a/lib/dolphin/gx/GXCpu2Efb.cpp +++ b/lib/dolphin/gx/GXCpu2Efb.cpp @@ -1,7 +1,9 @@ #include "gx.hpp" +#include "__gx.h" #include "../../gfx/depth_peek.hpp" +#include #include void GXPeekZ(u16 x, u16 y, u32* z) { @@ -10,9 +12,9 @@ void GXPeekZ(u16 x, u16 y, u32* z) { if (aurora::gfx::depth_peek::read_latest(x, y, value)) { *z = value; } else { - *z = g_gxState.clearDepth & 0x00ffffffu; + *z = 0; } } - aurora::gfx::depth_peek::request_snapshot(); + GX_WRITE_AURORA(GX_AURORA_REQUEST_DEPTH_SNAPSHOT); } diff --git a/lib/dolphin/gx/GXDispList.cpp b/lib/dolphin/gx/GXDispList.cpp index e165cd1..326bba5 100644 --- a/lib/dolphin/gx/GXDispList.cpp +++ b/lib/dolphin/gx/GXDispList.cpp @@ -1,7 +1,6 @@ #include "gx.hpp" #include "__gx.h" -#include "../../gx/command_processor.hpp" #include "../../gx/fifo.hpp" #include @@ -66,23 +65,4 @@ void GXCallDisplayList(const void* data, u32 nbytes) { // aurora::gx::fifo::process(static_cast(data), nbytes, true); } -void GXCallDisplayListLE(const void* data, u32 nbytes) { - // Flush any pending dirty state before calling - if (__gx->dirtyState != 0) { - __GXSetDirtyState(); - } - - // Flush pending primitives - if (*reinterpret_cast(&__gx->vNum) != 0) { - __GXSendFlushPrim(); - } - - // Drain the internal FIFO so that any pending CP register writes - // (VCD, VAT, etc.) are processed into g_gxState before the display - // list's draw commands reference them. - aurora::gx::fifo::drain(); - - // Process the display list through the command processor (little-endian) - aurora::gx::fifo::process(static_cast(data), nbytes, false); -} } diff --git a/lib/dolphin/gx/GXFrameBuffer.cpp b/lib/dolphin/gx/GXFrameBuffer.cpp index c196cf6..ad36cc4 100644 --- a/lib/dolphin/gx/GXFrameBuffer.cpp +++ b/lib/dolphin/gx/GXFrameBuffer.cpp @@ -31,6 +31,60 @@ aurora::Vec2 scale_copy_dst(u32 logicalWidth, u32 logicalHeight) { } } // namespace +namespace aurora::gx { +void copy_tex(const void* dest, GXBool clear) noexcept { + const auto rect = map_logical_scissor(g_gxState.texCopySrc); + const auto [dstWidth, dstHeight] = scale_copy_dst(g_gxState.texCopyDstWidth, g_gxState.texCopyDstHeight); + const auto texCopyFmt = g_gxState.texCopyFmt; + + const GXState::CopyTextureKey key{ + .dest = dest, + .width = dstWidth, + .height = dstHeight, + .format = texCopyFmt, + }; + auto it = g_gxState.copyTextureCache.find(key); + if (it == g_gxState.copyTextureCache.end()) { + gfx::TextureHandle handle; + if (gfx::tex_copy_conv::needs_conversion(texCopyFmt)) { + handle = gfx::new_conv_texture(dstWidth, dstHeight, texCopyFmt, "Copy Conv Texture"); + } else { + // Configure the texture swizzle to use alpha 1.0 if targeting RGB565 or EFB doesn't have alpha + const auto fmt = texCopyFmt == GX_TF_RGB565 || g_gxState.pixelFmt == GX_PF_RGB8_Z24 || + g_gxState.pixelFmt == GX_PF_RGB565_Z16 + ? GX_TF_RGB565 + : GX_TF_RGBA8; + handle = gfx::new_render_texture(dstWidth, dstHeight, fmt, "Resolved Texture"); + } + it = g_gxState.copyTextureCache.emplace(key, GXState::CopyTextureRef{.handle = handle, .revision = 0}).first; + } + auto& handle = it->second; + + if (g_gxState.alphaUpdate && g_gxState.dstAlpha != UINT32_MAX) { + if (!clear) { + // TODO: figure out the right behavior here. + // should the copy have a specific alpha value but the EFB remains untouched? + } + // Overwrite alpha before resolving + gfx::push_draw_command(gfx::clear::DrawData{ + .pipeline = gfx::pipeline_ref(gfx::clear::PipelineConfig{ + .clearColor = false, + .clearAlpha = true, + .clearDepth = false, + }), + .color = wgpu::Color{0.f, 0.f, 0.f, g_gxState.dstAlpha / 255.f}, + }); + } + const auto clearColor = clear && g_gxState.colorUpdate; + const auto clearAlpha = clear && g_gxState.alphaUpdate; + const auto clearDepth = clear && g_gxState.depthUpdate; + gfx::resolve_pass(handle.handle, rect, clearColor, clearAlpha, clearDepth, g_gxState.clearColor, clear_depth_value(), + texCopyFmt); + ++handle.revision; + g_gxState.copyTextures[dest] = handle; +} +} // namespace aurora::gx + extern "C" { GXRenderModeObj GXNtsc480IntDf = { VI_TVMODE_NTSC_INT, @@ -103,14 +157,22 @@ void GXAdjustForOverscan(GXRenderModeObj* rmin, GXRenderModeObj* rmout, u16 hor, void GXSetDispCopySrc(u16 left, u16 top, u16 wd, u16 ht) {} -void GXSetTexCopySrc(u16 left, u16 top, u16 wd, u16 ht) { g_gxState.texCopySrc = {left, top, wd, ht}; } +void GXSetTexCopySrc(u16 left, u16 top, u16 wd, u16 ht) { + GX_WRITE_AURORA(GX_AURORA_LOAD_COPY_SRC); + GX_WRITE_U32(left); + GX_WRITE_U32(top); + GX_WRITE_U32(wd); + GX_WRITE_U32(ht); +} void GXSetDispCopyDst(u16 wd, u16 ht) {} void GXSetTexCopyDst(u16 wd, u16 ht, GXTexFmt fmt, GXBool mipmap) { - g_gxState.texCopyFmt = fmt; - g_gxState.texCopyDstWidth = wd; - g_gxState.texCopyDstHeight = ht; + GX_WRITE_AURORA(GX_AURORA_LOAD_COPY_DST); + GX_WRITE_U32(wd); + GX_WRITE_U32(ht); + GX_WRITE_U32(fmt); + GX_WRITE_U8(mipmap != GX_FALSE); } // TODO GXSetDispCopyFrame2Field @@ -148,57 +210,13 @@ void GXSetDispCopyGamma(GXGamma gamma) {} void GXCopyDisp(void* dest, GXBool clear) {} void GXCopyTex(void* dest, GXBool clear) { - aurora::gx::fifo::drain(); + GX_WRITE_AURORA(GX_AURORA_LOAD_COPY_DEST); + GX_WRITE_U64(reinterpret_cast(dest)); - const auto rect = aurora::gx::map_logical_scissor(g_gxState.texCopySrc); - const auto [dstWidth, dstHeight] = scale_copy_dst(g_gxState.texCopyDstWidth, g_gxState.texCopyDstHeight); - const auto texCopyFmt = g_gxState.texCopyFmt; - - const aurora::gx::GXState::CopyTextureKey key{ - .dest = dest, - .width = dstWidth, - .height = dstHeight, - .format = texCopyFmt, - }; - auto it = g_gxState.copyTextureCache.find(key); - if (it == g_gxState.copyTextureCache.end()) { - aurora::gfx::TextureHandle handle; - if (aurora::gfx::tex_copy_conv::needs_conversion(texCopyFmt)) { - handle = aurora::gfx::new_conv_texture(dstWidth, dstHeight, texCopyFmt, "Copy Conv Texture"); - } else { - // Configure the texture swizzle to use alpha 1.0 if targeting RGB565 or EFB doesn't have alpha - const auto fmt = texCopyFmt == GX_TF_RGB565 || g_gxState.pixelFmt == GX_PF_RGB8_Z24 || - g_gxState.pixelFmt == GX_PF_RGB565_Z16 - ? GX_TF_RGB565 - : GX_TF_RGBA8; - handle = aurora::gfx::new_render_texture(dstWidth, dstHeight, fmt, "Resolved Texture"); - } - it = g_gxState.copyTextureCache.emplace(key, aurora::gx::GXState::CopyTextureRef{.handle = handle, .revision = 0}).first; - } - auto& handle = it->second; - - if (g_gxState.alphaUpdate && g_gxState.dstAlpha != UINT32_MAX) { - if (!clear) { - // TODO: figure out the right behavior here. - // should the copy have a specific alpha value but the EFB remains untouched? - } - // Overwrite alpha before resolving - aurora::gfx::push_draw_command(aurora::gfx::clear::DrawData{ - .pipeline = aurora::gfx::pipeline_ref(aurora::gfx::clear::PipelineConfig{ - .clearColor = false, - .clearAlpha = true, - .clearDepth = false, - }), - .color = wgpu::Color{0.f, 0.f, 0.f, g_gxState.dstAlpha / 255.f}, - }); - } - const auto clearColor = clear && g_gxState.colorUpdate; - const auto clearAlpha = clear && g_gxState.alphaUpdate; - const auto clearDepth = clear && g_gxState.depthUpdate; - aurora::gfx::resolve_pass(handle.handle, rect, clearColor, clearAlpha, clearDepth, g_gxState.clearColor, - aurora::gx::clear_depth_value(), texCopyFmt); - ++handle.revision; - g_gxState.copyTextures[dest] = handle; + SET_REG_FIELD(0, __gx->cpTex, 1, 11, clear != GX_FALSE); + SET_REG_FIELD(0, __gx->cpTex, 1, 14, 0); + SET_REG_FIELD(0, __gx->cpTex, 8, 24, 0x52); + GX_WRITE_RAS_REG(__gx->cpTex); } // TODO GXGetYScaleFactor diff --git a/lib/dolphin/gx/GXManage.cpp b/lib/dolphin/gx/GXManage.cpp index 84c3cfc..659c88d 100644 --- a/lib/dolphin/gx/GXManage.cpp +++ b/lib/dolphin/gx/GXManage.cpp @@ -71,6 +71,7 @@ GXFifoObj* GXInit(void* base, u32 size) { SET_REG_FIELD(0, __gx->cmode1, 8, 24, 0x42); SET_REG_FIELD(0, __gx->zmode, 8, 24, 0x40); SET_REG_FIELD(0, __gx->peCtrl, 8, 24, 0x43); + SET_REG_FIELD(0, __gx->cpTex, 2, 7, 0); SET_REG_FIELD(0, __gx->IndTexScale0, 8, 24, 0x25); SET_REG_FIELD(0, __gx->IndTexScale1, 8, 24, 0x26); diff --git a/lib/dolphin/gx/__gx.h b/lib/dolphin/gx/__gx.h index 5be1395..253fade 100644 --- a/lib/dolphin/gx/__gx.h +++ b/lib/dolphin/gx/__gx.h @@ -83,8 +83,7 @@ GX_WRITE_U32(value); \ } while (0) -// Shadow register struct - mirrors the hardware GX state -// This is the subset of __GXData_struct needed for aurora's TARGET_PC emulation. +// Shadow register struct struct __GXData_struct { u16 vNum; // vertex count for flush prim u16 bpSent; // BP register was sent (need flush prim before next draw) @@ -126,6 +125,7 @@ struct __GXData_struct { u32 cmode1; // destination alpha register u32 zmode; // Z-buffer mode register u32 peCtrl; // pixel engine control + u32 cpTex; // copy texture trigger register u32 genMode; // general mode (numTexGens, numChans, numTevStages, cullMode, numIndStages) diff --git a/lib/gx/command_processor.cpp b/lib/gx/command_processor.cpp index 21e177d..cd12369 100644 --- a/lib/gx/command_processor.cpp +++ b/lib/gx/command_processor.cpp @@ -1,6 +1,7 @@ #include "command_processor.hpp" #include "../gfx/common.hpp" +#include "../gfx/depth_peek.hpp" #include "dolphin/gx/GXAurora.h" #include "gx.hpp" #include "gx_fmt.hpp" @@ -12,6 +13,7 @@ #include #include +#include #include #include @@ -502,7 +504,7 @@ static void handle_bp(u32 value, bool bigEndian) { g_gxState.bpRegCache[0xFE] = 0x00FFFFFF; const u32 merged = (g_gxState.bpRegCache[regId] & ~ssMask) | (value & ssMask); value = (regId << 24) | (merged & 0x00FFFFFF); - if (g_gxState.bpRegCache[regId] == value) + if (g_gxState.bpRegCache[regId] == value && regId != 0x52) return; g_gxState.bpRegCache[regId] = value; } @@ -785,6 +787,17 @@ static void handle_bp(u32 value, bool bigEndian) { break; } + // Texture copy + case 0x52: { + const bool clear = bp_get(value, 1, 11) != 0; + if (bp_get(value, 1, 14) != 0) { + Log.warn("STUB: display copy is not implemented"); + } else { + copy_tex(g_gxState.texCopyDest, clear); + } + break; + } + // TLUT load address / execute (0x64, 0x65) case 0x64: break; @@ -1808,6 +1821,41 @@ void handle_aurora(const u8* data, u32& pos, u32 size, bool bigEndian) { g_gxState.clamp = read_f32(data + pos, bigEndian); pos += 4; g_gxState.stateDirty = true; + } else if (subCmd == GX_AURORA_LOAD_COPY_SRC) { + CHECK(pos + 16 <= size, "GX_AURORA_LOAD_COPY_SRC read overrun"); + const int32_t left = static_cast(read_u32(data + pos, bigEndian)); + pos += 4; + const int32_t top = static_cast(read_u32(data + pos, bigEndian)); + pos += 4; + const int32_t width = static_cast(read_u32(data + pos, bigEndian)); + pos += 4; + const int32_t height = static_cast(read_u32(data + pos, bigEndian)); + pos += 4; + g_gxState.texCopySrc = {left, top, width, height}; + } else if (subCmd == GX_AURORA_LOAD_COPY_DST) { + CHECK(pos + 13 <= size, "GX_AURORA_LOAD_COPY_DST read overrun"); + g_gxState.texCopyDstWidth = read_u32(data + pos, bigEndian); + pos += 4; + g_gxState.texCopyDstHeight = read_u32(data + pos, bigEndian); + pos += 4; + g_gxState.texCopyFmt = static_cast(read_u32(data + pos, bigEndian)); + pos += 4; + g_gxState.texCopyDstWide = true; + } else if (subCmd == GX_AURORA_LOAD_COPY_DEST) { + CHECK(pos + 8 <= size, "GX_AURORA_LOAD_COPY_DEST read overrun"); + g_gxState.texCopyDest = reinterpret_cast(read_u64(data + pos, bigEndian)); + pos += 8; + } else if (subCmd == GX_AURORA_REQUEST_DEPTH_SNAPSHOT) { + gfx::depth_peek::request_snapshot(); + } else if (subCmd == GX_AURORA_BEGIN_OFFSCREEN) { + CHECK(pos + 8 <= size, "GX_AURORA_BEGIN_OFFSCREEN read overrun"); + const u32 width = read_u32(data + pos, bigEndian); + pos += 4; + const u32 height = read_u32(data + pos, bigEndian); + pos += 4; + gfx::begin_offscreen(width, height); + } else if (subCmd == GX_AURORA_END_OFFSCREEN) { + gfx::end_offscreen(); } else if (subCmd == GX_AURORA_DESTROY_TEXOBJ) { CHECK(pos + 4 <= size, "GX_AURORA_DESTROY_TEXOBJ read overrun"); evict_texture_object(read_u32(data + pos, bigEndian)); diff --git a/lib/gx/gx.hpp b/lib/gx/gx.hpp index 287bbd7..e73841a 100644 --- a/lib/gx/gx.hpp +++ b/lib/gx/gx.hpp @@ -345,8 +345,10 @@ struct GXState { std::array arrays; gfx::ClipRect texCopySrc; GXTexFmt texCopyFmt; - u16 texCopyDstWidth = 0; - u16 texCopyDstHeight = 0; + u32 texCopyDstWidth = 0; + u32 texCopyDstHeight = 0; + bool texCopyDstWide = false; + const void* texCopyDest = nullptr; struct CopyTextureKey { const void* dest = nullptr; u32 width = 0; @@ -406,6 +408,7 @@ void set_logical_viewport(const gfx::Viewport& viewport) noexcept; void set_render_viewport(const gfx::Viewport& viewport) noexcept; void set_logical_scissor(const gfx::ClipRect& scissor) noexcept; void set_render_scissor(const gfx::ClipRect& scissor) noexcept; +void copy_tex(const void* dest, GXBool clear) noexcept; const gfx::TextureBind& get_texture(GXTexMapID id) noexcept; void resolve_sampled_textures(const ShaderInfo& info) noexcept;