Reimplement GXCopyTex; simplify assertions

This commit is contained in:
Luke Street 2022-08-09 02:05:33 -04:00
parent 893cabe55a
commit c060e1da6b
24 changed files with 241 additions and 394 deletions

View File

@ -37,6 +37,7 @@ typedef struct {
float scale; float scale;
} AuroraWindowSize; } AuroraWindowSize;
typedef struct SDL_Window SDL_Window;
typedef struct AuroraEvent AuroraEvent; typedef struct AuroraEvent AuroraEvent;
typedef void (*AuroraLogCallback)(AuroraLogLevel level, const char* message, unsigned int len); typedef void (*AuroraLogCallback)(AuroraLogLevel level, const char* message, unsigned int len);
@ -61,6 +62,7 @@ typedef struct {
typedef struct { typedef struct {
AuroraBackend backend; AuroraBackend backend;
const char* configPath; const char* configPath;
SDL_Window* window;
AuroraWindowSize windowSize; AuroraWindowSize windowSize;
} AuroraInfo; } AuroraInfo;

View File

@ -87,17 +87,11 @@ static AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config)
} }
} }
if (!windowCreated) { ASSERT(windowCreated, "Error creating window: {}", SDL_GetError());
Log.report(LOG_FATAL, FMT_STRING("Error creating window: {}"), SDL_GetError());
unreachable();
}
// Initialize SDL_Renderer for ImGui when we can't use a Dawn backend // Initialize SDL_Renderer for ImGui when we can't use a Dawn backend
if (webgpu::g_backendType == wgpu::BackendType::Null) { if (webgpu::g_backendType == wgpu::BackendType::Null) {
if (!window::create_renderer()) { ASSERT(window::create_renderer(), "Failed to initialize SDL renderer: {}", SDL_GetError());
Log.report(LOG_FATAL, FMT_STRING("Failed to initialize SDL renderer: {}"), SDL_GetError());
unreachable();
}
} }
window::show_window(); window::show_window();
@ -117,6 +111,7 @@ static AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config)
return { return {
.backend = selectedBackend, .backend = selectedBackend,
.configPath = g_config.configPath, .configPath = g_config.configPath,
.window = window::get_sdl_window(),
.windowSize = size, .windowSize = size,
}; };
} }

View File

@ -31,9 +31,8 @@ private:
void CreateSwapChainImpl() { void CreateSwapChainImpl() {
VkSurfaceKHR surface = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE;
if (SDL_Vulkan_CreateSurface(m_window, dawn::native::vulkan::GetInstance(m_device), &surface) != SDL_TRUE) { ASSERT(SDL_Vulkan_CreateSurface(m_window, dawn::native::vulkan::GetInstance(m_device), &surface),
Log.report(LOG_FATAL, FMT_STRING("Failed to create Vulkan surface: {}"), SDL_GetError()); "Failed to create Vulkan surface: {}", SDL_GetError());
}
m_swapChainImpl = dawn::native::vulkan::CreateNativeSwapChainImpl(m_device, surface); m_swapChainImpl = dawn::native::vulkan::CreateNativeSwapChainImpl(m_device, surface);
} }
}; };

View File

@ -15,9 +15,7 @@ void GXSetIndTexCoordScale(GXIndTexStageID indStage, GXIndTexScale scaleS, GXInd
} }
void GXSetIndTexMtx(GXIndTexMtxID id, const void* offset, s8 scaleExp) { void GXSetIndTexMtx(GXIndTexMtxID id, const void* offset, s8 scaleExp) {
if (id < GX_ITM_0 || id > GX_ITM_2) { CHECK(id >= GX_ITM_0 && id <= GX_ITM_2, "invalid ind tex mtx ID {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("invalid ind tex mtx ID {}"), id);
}
update_gx_state(g_gxState.indTexMtxs[id - 1], {*reinterpret_cast<const aurora::Mat3x2<float>*>(offset), scaleExp}); update_gx_state(g_gxState.indTexMtxs[id - 1], {*reinterpret_cast<const aurora::Mat3x2<float>*>(offset), scaleExp});
} }

View File

@ -1,6 +1,7 @@
#include "gx.hpp" #include "gx.hpp"
#include "../window.hpp" #include "../window.hpp"
#include "../webgpu/wgpu.hpp"
extern "C" { extern "C" {
GXRenderModeObj GXNtsc480IntDf = { GXRenderModeObj GXNtsc480IntDf = {
@ -24,14 +25,13 @@ void GXAdjustForOverscan(GXRenderModeObj* rmin, GXRenderModeObj* rmout, u16 hor,
void GXSetDispCopySrc(u16 left, u16 top, u16 wd, u16 ht) {} void GXSetDispCopySrc(u16 left, u16 top, u16 wd, u16 ht) {}
void GXSetTexCopySrc(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}; }
// TODO
}
void GXSetDispCopyDst(u16 wd, u16 ht) {} void GXSetDispCopyDst(u16 wd, u16 ht) {}
void GXSetTexCopyDst(u16 wd, u16 ht, GXTexFmt fmt, GXBool mipmap) { void GXSetTexCopyDst(u16 wd, u16 ht, GXTexFmt fmt, GXBool mipmap) {
// TODO CHECK(wd == g_gxState.texCopySrc.width && ht == g_gxState.texCopySrc.height, "Texture copy scaling unimplemented");
g_gxState.texCopyFmt = fmt;
} }
// TODO GXSetDispCopyFrame2Field // TODO GXSetDispCopyFrame2Field
@ -47,7 +47,23 @@ void GXSetDispCopyGamma(GXGamma gamma) {}
void GXCopyDisp(void* dest, GXBool clear) {} void GXCopyDisp(void* dest, GXBool clear) {}
// TODO move GXCopyTex here void GXCopyTex(void* dest, GXBool clear) {
const auto& rect = g_gxState.texCopySrc;
const wgpu::Extent3D size{
.width = static_cast<uint32_t>(rect.width),
.height = static_cast<uint32_t>(rect.height),
.depthOrArrayLayers = 1,
};
aurora::gfx::TextureHandle handle;
const auto it = g_gxState.copyTextures.find(dest);
if (it == g_gxState.copyTextures.end() || it->second->size != size) {
handle = aurora::gfx::new_render_texture(rect.width, rect.height, g_gxState.texCopyFmt, "Resolved Texture");
g_gxState.copyTextures[dest] = handle;
} else {
handle = it->second;
}
aurora::gfx::resolve_pass(handle, rect, clear, g_gxState.clearColor);
}
// TODO GXGetYScaleFactor // TODO GXGetYScaleFactor
// TODO GXGetNumXfbLines // TODO GXGetNumXfbLines

View File

@ -15,14 +15,8 @@ void GXSetVtxDescv(GXVtxDescList* list) {
void GXClearVtxDesc() { g_gxState.vtxDesc.fill({}); } void GXClearVtxDesc() { g_gxState.vtxDesc.fill({}); }
void GXSetVtxAttrFmt(GXVtxFmt vtxfmt, GXAttr attr, GXCompCnt cnt, GXCompType type, u8 frac) { void GXSetVtxAttrFmt(GXVtxFmt vtxfmt, GXAttr attr, GXCompCnt cnt, GXCompType type, u8 frac) {
if (vtxfmt < GX_VTXFMT0 || vtxfmt >= GX_MAX_VTXFMT) { CHECK(vtxfmt >= GX_VTXFMT0 && vtxfmt < GX_MAX_VTXFMT, "invalid vtxfmt {}", static_cast<int>(vtxfmt));
Log.report(LOG_FATAL, FMT_STRING("invalid vtxfmt {}"), vtxfmt); CHECK(attr >= GX_VA_PNMTXIDX && attr < GX_VA_MAX_ATTR, "invalid attr {}", static_cast<int>(attr));
unreachable();
}
if (attr < GX_VA_PNMTXIDX || attr >= GX_VA_MAX_ATTR) {
Log.report(LOG_FATAL, FMT_STRING("invalid attr {}"), attr);
unreachable();
}
auto& fmt = g_gxState.vtxFmts[vtxfmt].attrs[attr]; auto& fmt = g_gxState.vtxFmts[vtxfmt].attrs[attr];
update_gx_state(fmt.cnt, cnt); update_gx_state(fmt.cnt, cnt);
update_gx_state(fmt.type, type); update_gx_state(fmt.type, type);
@ -42,10 +36,7 @@ void GXSetArray(GXAttr attr, const void* data, u32 size, u8 stride) {
// TODO move GXBegin, GXEnd here // TODO move GXBegin, GXEnd here
void GXSetTexCoordGen2(GXTexCoordID dst, GXTexGenType type, GXTexGenSrc src, u32 mtx, GXBool normalize, u32 postMtx) { void GXSetTexCoordGen2(GXTexCoordID dst, GXTexGenType type, GXTexGenSrc src, u32 mtx, GXBool normalize, u32 postMtx) {
if (dst < GX_TEXCOORD0 || dst > GX_TEXCOORD7) { CHECK(dst >= GX_TEXCOORD0 && dst <= GX_TEXCOORD7, "invalid tex coord {}", static_cast<int>(dst));
Log.report(LOG_FATAL, FMT_STRING("invalid tex coord {}"), dst);
unreachable();
}
update_gx_state(g_gxState.tcgs[dst], update_gx_state(g_gxState.tcgs[dst],
{type, src, static_cast<GXTexMtx>(mtx), static_cast<GXPTTexMtx>(postMtx), normalize}); {type, src, static_cast<GXTexMtx>(mtx), static_cast<GXPTTexMtx>(postMtx), normalize});
} }

View File

@ -152,10 +152,7 @@ void GXSetChanAmbColor(GXChannelID id, GXColor color) {
GXSetChanAmbColor(GX_ALPHA1, color); GXSetChanAmbColor(GX_ALPHA1, color);
return; return;
} }
if (id < GX_COLOR0 || id > GX_ALPHA1) { CHECK(id >= GX_COLOR0 && id <= GX_ALPHA1, "bad channel {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("bad channel {}"), id);
unreachable();
}
update_gx_state(g_gxState.colorChannelState[id].ambColor, from_gx_color(color)); update_gx_state(g_gxState.colorChannelState[id].ambColor, from_gx_color(color));
} }
@ -169,10 +166,7 @@ void GXSetChanMatColor(GXChannelID id, GXColor color) {
GXSetChanMatColor(GX_ALPHA1, color); GXSetChanMatColor(GX_ALPHA1, color);
return; return;
} }
if (id < GX_COLOR0 || id > GX_ALPHA1) { CHECK(id >= GX_COLOR0 && id <= GX_ALPHA1, "bad channel {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("bad channel {}"), id);
unreachable();
}
update_gx_state(g_gxState.colorChannelState[id].matColor, from_gx_color(color)); update_gx_state(g_gxState.colorChannelState[id].matColor, from_gx_color(color));
} }
@ -224,10 +218,7 @@ void GXSetChanCtrl(GXChannelID id, bool lightingEnabled, GXColorSrc ambSrc, GXCo
GXSetChanCtrl(GX_ALPHA1, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn); GXSetChanCtrl(GX_ALPHA1, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn);
return; return;
} }
if (id < GX_COLOR0 || id > GX_ALPHA1) { CHECK(id >= GX_COLOR0 && id <= GX_ALPHA1, "bad channel {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("bad channel {}"), id);
unreachable();
}
auto& chan = g_gxState.colorChannelConfig[id]; auto& chan = g_gxState.colorChannelConfig[id];
update_gx_state(chan.lightingEnabled, lightingEnabled); update_gx_state(chan.lightingEnabled, lightingEnabled);
update_gx_state(chan.ambSrc, ambSrc); update_gx_state(chan.ambSrc, ambSrc);

View File

@ -52,10 +52,7 @@ void GXSetTevAlphaOp(GXTevStageID stageId, GXTevOp op, GXTevBias bias, GXTevScal
} }
void GXSetTevColor(GXTevRegID id, GXColor color) { void GXSetTevColor(GXTevRegID id, GXColor color) {
if (id < GX_TEVPREV || id > GX_TEVREG2) { CHECK(id >= GX_TEVPREV && id < GX_MAX_TEVREG, "bad tevreg {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("bad tevreg {}"), id);
unreachable();
}
update_gx_state(g_gxState.colorRegs[id], from_gx_color(color)); update_gx_state(g_gxState.colorRegs[id], from_gx_color(color));
} }
@ -84,10 +81,7 @@ void GXSetTevOrder(GXTevStageID id, GXTexCoordID tcid, GXTexMapID tmid, GXChanne
void GXSetNumTevStages(u8 num) { update_gx_state(g_gxState.numTevStages, num); } void GXSetNumTevStages(u8 num) { update_gx_state(g_gxState.numTevStages, num); }
void GXSetTevKColor(GXTevKColorID id, GXColor color) { void GXSetTevKColor(GXTevKColorID id, GXColor color) {
if (id >= GX_MAX_KCOLOR) { CHECK(id >= GX_KCOLOR0 && id < GX_MAX_KCOLOR, "bad kcolor {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("bad kcolor {}"), id);
unreachable();
}
update_gx_state(g_gxState.kcolors[id], from_gx_color(color)); update_gx_state(g_gxState.kcolors[id], from_gx_color(color));
} }
@ -103,9 +97,6 @@ void GXSetTevSwapMode(GXTevStageID stageId, GXTevSwapSel rasSel, GXTevSwapSel te
void GXSetTevSwapModeTable(GXTevSwapSel id, GXTevColorChan red, GXTevColorChan green, GXTevColorChan blue, void GXSetTevSwapModeTable(GXTevSwapSel id, GXTevColorChan red, GXTevColorChan green, GXTevColorChan blue,
GXTevColorChan alpha) { GXTevColorChan alpha) {
if (id < GX_TEV_SWAP0 || id >= GX_MAX_TEVSWAP) { CHECK(id >= GX_TEV_SWAP0 && id < GX_MAX_TEVSWAP, "bad tev swap sel {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("invalid tev swap sel {}"), id);
unreachable();
}
update_gx_state(g_gxState.tevSwapTable[id], {red, green, blue, alpha}); update_gx_state(g_gxState.tevSwapTable[id], {red, green, blue, alpha});
} }

View File

@ -4,8 +4,6 @@
#include <absl/container/flat_hash_map.h> #include <absl/container/flat_hash_map.h>
static absl::flat_hash_map<void*, int> g_resolvedTexMap;
void GXInitTexObj(GXTexObj* obj_, const void* data, u16 width, u16 height, u32 format, GXTexWrapMode wrapS, void GXInitTexObj(GXTexObj* obj_, const void* data, u16 width, u16 height, u32 format, GXTexWrapMode wrapS,
GXTexWrapMode wrapT, GXBool mipmap) { GXTexWrapMode wrapT, GXBool mipmap) {
memset(obj_, 0, sizeof(GXTexObj)); memset(obj_, 0, sizeof(GXTexObj));
@ -27,8 +25,10 @@ void GXInitTexObj(GXTexObj* obj_, const void* data, u16 width, u16 height, u32 f
obj->doEdgeLod = false; obj->doEdgeLod = false;
obj->maxAniso = GX_ANISO_4; obj->maxAniso = GX_ANISO_4;
obj->tlut = GX_TLUT0; obj->tlut = GX_TLUT0;
if (g_resolvedTexMap.contains(data)) { const auto it = g_gxState.copyTextures.find(data);
obj->dataInvalidated = false; // TODO hack if (it != g_gxState.copyTextures.end()) {
obj->ref = it->second;
obj->dataInvalidated = false;
} else { } else {
obj->dataInvalidated = true; obj->dataInvalidated = true;
} }
@ -55,7 +55,13 @@ void GXInitTexObjCI(GXTexObj* obj_, const void* data, u16 width, u16 height, GXC
obj->biasClamp = false; obj->biasClamp = false;
obj->doEdgeLod = false; obj->doEdgeLod = false;
obj->maxAniso = GX_ANISO_4; obj->maxAniso = GX_ANISO_4;
const auto it = g_gxState.copyTextures.find(data);
if (it != g_gxState.copyTextures.end()) {
obj->ref = it->second;
obj->dataInvalidated = false;
} else {
obj->dataInvalidated = true; obj->dataInvalidated = true;
}
} }
void GXInitTexObjLOD(GXTexObj* obj_, GXTexFilter minFilt, GXTexFilter magFilt, float minLod, float maxLod, void GXInitTexObjLOD(GXTexObj* obj_, GXTexFilter minFilt, GXTexFilter magFilt, float minLod, float maxLod,
@ -73,8 +79,14 @@ void GXInitTexObjLOD(GXTexObj* obj_, GXTexFilter minFilt, GXTexFilter magFilt, f
void GXInitTexObjData(GXTexObj* obj_, const void* data) { void GXInitTexObjData(GXTexObj* obj_, const void* data) {
auto* obj = reinterpret_cast<GXTexObj_*>(obj_); auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
const auto it = g_gxState.copyTextures.find(data);
if (it != g_gxState.copyTextures.end()) {
obj->ref = it->second;
obj->dataInvalidated = false;
} else {
obj->data = data; obj->data = data;
obj->dataInvalidated = true; obj->dataInvalidated = true;
}
} }
void GXInitTexObjWrapMode(GXTexObj* obj_, GXTexWrapMode wrapS, GXTexWrapMode wrapT) { void GXInitTexObjWrapMode(GXTexObj* obj_, GXTexWrapMode wrapS, GXTexWrapMode wrapT) {
@ -184,6 +196,7 @@ void GXInitTlutObj(GXTlutObj* obj_, const void* data, GXTlutFmt format, u16 entr
memset(obj_, 0, sizeof(GXTlutObj)); memset(obj_, 0, sizeof(GXTlutObj));
GXTexFmt texFmt; GXTexFmt texFmt;
switch (format) { switch (format) {
DEFAULT_FATAL("invalid tlut format {}", static_cast<int>(format));
case GX_TL_IA8: case GX_TL_IA8:
texFmt = GX_TF_IA8; texFmt = GX_TF_IA8;
break; break;
@ -193,9 +206,6 @@ void GXInitTlutObj(GXTlutObj* obj_, const void* data, GXTlutFmt format, u16 entr
case GX_TL_RGB5A3: case GX_TL_RGB5A3:
texFmt = GX_TF_RGB5A3; texFmt = GX_TF_RGB5A3;
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid tlut format {}"), format);
unreachable();
} }
auto* obj = reinterpret_cast<GXTlutObj_*>(obj_); auto* obj = reinterpret_cast<GXTlutObj_*>(obj_);
obj->ref = aurora::gfx::new_static_texture_2d( obj->ref = aurora::gfx::new_static_texture_2d(
@ -224,8 +234,3 @@ void GXInvalidateTexAll() {
// TODO GXSetTexCoordScaleManually // TODO GXSetTexCoordScaleManually
// TODO GXSetTexCoordCylWrap // TODO GXSetTexCoordCylWrap
// TODO GXSetTexCoordBias // TODO GXSetTexCoordBias
void GXCopyTex(void* dest, GXBool clear) {
// TODO
g_resolvedTexMap.emplace(dest, 0);
}

View File

@ -21,10 +21,7 @@ void GXSetProjection(const void* mtx_, GXProjectionType type) {
// TODO GXSetProjectionv // TODO GXSetProjectionv
void GXLoadPosMtxImm(const void* mtx_, u32 id) { void GXLoadPosMtxImm(const void* mtx_, u32 id) {
if (id < GX_PNMTX0 || id > GX_PNMTX9) { CHECK(id >= GX_PNMTX0 && id <= GX_PNMTX9, "invalid pn mtx {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("invalid pn mtx {}"), id);
unreachable();
}
auto& state = g_gxState.pnMtx[id / 3]; auto& state = g_gxState.pnMtx[id / 3];
#ifdef AURORA_NATIVE_MATRIX #ifdef AURORA_NATIVE_MATRIX
const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_); const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_);
@ -38,10 +35,7 @@ void GXLoadPosMtxImm(const void* mtx_, u32 id) {
// TODO GXLoadPosMtxIndx // TODO GXLoadPosMtxIndx
void GXLoadNrmMtxImm(const void* mtx_, u32 id) { void GXLoadNrmMtxImm(const void* mtx_, u32 id) {
if (id < GX_PNMTX0 || id > GX_PNMTX9) { CHECK(id >= GX_PNMTX0 && id <= GX_PNMTX9, "invalid pn mtx {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("invalid pn mtx {}"), id);
unreachable();
}
auto& state = g_gxState.pnMtx[id / 3]; auto& state = g_gxState.pnMtx[id / 3];
#ifdef AURORA_NATIVE_MATRIX #ifdef AURORA_NATIVE_MATRIX
const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_); const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_);
@ -56,23 +50,15 @@ void GXLoadNrmMtxImm(const void* mtx_, u32 id) {
// TODO GXLoadNrmMtxIndx3x3 // TODO GXLoadNrmMtxIndx3x3
void GXSetCurrentMtx(u32 id) { void GXSetCurrentMtx(u32 id) {
if (id < GX_PNMTX0 || id > GX_PNMTX9) { CHECK(id >= GX_PNMTX0 && id <= GX_PNMTX9, "invalid pn mtx {}", static_cast<int>(id));
Log.report(LOG_FATAL, FMT_STRING("invalid pn mtx {}"), id);
unreachable();
}
update_gx_state(g_gxState.currentPnMtx, id / 3); update_gx_state(g_gxState.currentPnMtx, id / 3);
} }
void GXLoadTexMtxImm(const void* mtx_, u32 id, GXTexMtxType type) { void GXLoadTexMtxImm(const void* mtx_, u32 id, GXTexMtxType type) {
if ((id < GX_TEXMTX0 || id > GX_IDENTITY) && (id < GX_PTTEXMTX0 || id > GX_PTIDENTITY)) { CHECK((id >= GX_TEXMTX0 && id <= GX_IDENTITY) || (id >= GX_PTTEXMTX0 && id <= GX_PTIDENTITY), "invalid tex mtx {}",
Log.report(LOG_FATAL, FMT_STRING("invalid tex mtx {}"), id); static_cast<int>(id));
unreachable();
}
if (id >= GX_PTTEXMTX0) { if (id >= GX_PTTEXMTX0) {
if (type != GX_MTX3x4) { CHECK(type == GX_MTX3x4, "invalid pt mtx type {}", type);
Log.report(LOG_FATAL, FMT_STRING("invalid pt mtx type {}"), type);
unreachable();
}
const auto idx = (id - GX_PTTEXMTX0) / 3; const auto idx = (id - GX_PTTEXMTX0) / 3;
#ifdef AURORA_NATIVE_MATRIX #ifdef AURORA_NATIVE_MATRIX
const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_); const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_);

View File

@ -48,12 +48,7 @@ static std::optional<SStreamState> sStreamState;
static u16 lastVertexStart = 0; static u16 lastVertexStart = 0;
void GXBegin(GXPrimitive primitive, GXVtxFmt vtxFmt, u16 nVerts) { void GXBegin(GXPrimitive primitive, GXVtxFmt vtxFmt, u16 nVerts) {
#ifndef NDEBUG CHECK(!sStreamState, "Stream began twice!");
if (sStreamState) {
Log.report(LOG_FATAL, FMT_STRING("Stream began twice!"));
unreachable();
}
#endif
uint16_t vertexSize = 0; uint16_t vertexSize = 0;
for (GXAttr attr{}; const auto type : g_gxState.vtxDesc) { for (GXAttr attr{}; const auto type : g_gxState.vtxDesc) {
if (type == GX_DIRECT) { if (type == GX_DIRECT) {
@ -63,32 +58,23 @@ void GXBegin(GXPrimitive primitive, GXVtxFmt vtxFmt, u16 nVerts) {
vertexSize += 16; vertexSize += 16;
} else if (attr >= GX_VA_TEX0 && attr <= GX_VA_TEX7) { } else if (attr >= GX_VA_TEX0 && attr <= GX_VA_TEX7) {
vertexSize += 8; vertexSize += 8;
} else { } else UNLIKELY {
Log.report(LOG_FATAL, FMT_STRING("don't know how to handle attr {}"), attr); FATAL("dont know how to handle attr {}", static_cast<int>(attr));
unreachable();
} }
} else if (type == GX_INDEX8 || type == GX_INDEX16) { } else if (type == GX_INDEX8 || type == GX_INDEX16) {
vertexSize += 2; vertexSize += 2;
} }
attr = GXAttr(attr + 1); attr = GXAttr(attr + 1);
} }
if (vertexSize == 0) { CHECK(vertexSize > 0, "no vtx attributes enabled?");
Log.report(LOG_FATAL, FMT_STRING("no vtx attributes enabled?"));
unreachable();
}
sStreamState.emplace(primitive, nVerts, vertexSize, g_gxState.stateDirty ? 0 : lastVertexStart); sStreamState.emplace(primitive, nVerts, vertexSize, g_gxState.stateDirty ? 0 : lastVertexStart);
} }
static inline void check_attr_order(GXAttr attr) noexcept { static inline void check_attr_order(GXAttr attr) noexcept {
#ifndef NDEBUG #ifndef NDEBUG
if (!sStreamState) { CHECK(sStreamState, "Stream not started!");
Log.report(LOG_FATAL, FMT_STRING("Stream not started!")); CHECK(sStreamState->nextAttr == attr, "bad attribute order: {}, expected {}", static_cast<int>(attr),
unreachable(); static_cast<int>(sStreamState->nextAttr));
}
if (sStreamState->nextAttr != attr) {
Log.report(LOG_FATAL, FMT_STRING("bad attribute order: {}, expected {}"), attr, sStreamState->nextAttr);
unreachable();
}
sStreamState->nextAttr = next_attr(attr + 1); sStreamState->nextAttr = next_attr(attr + 1);
#endif #endif
} }

View File

@ -143,14 +143,8 @@ size_t g_lastIndexSize;
size_t g_lastStorageSize; size_t g_lastStorageSize;
using CommandList = std::vector<Command>; using CommandList = std::vector<Command>;
struct ClipRect {
int32_t x;
int32_t y;
int32_t width;
int32_t height;
};
struct RenderPass { struct RenderPass {
u32 resolveTarget = UINT32_MAX; TextureHandle resolveTarget;
ClipRect resolveRect; ClipRect resolveRect;
Vec4<float> clearColor{0.f, 0.f, 0.f, 0.f}; Vec4<float> clearColor{0.f, 0.f, 0.f, 0.f};
CommandList commands; CommandList commands;
@ -158,7 +152,6 @@ struct RenderPass {
}; };
static std::vector<RenderPass> g_renderPasses; static std::vector<RenderPass> g_renderPasses;
static u32 g_currentRenderPass = UINT32_MAX; static u32 g_currentRenderPass = UINT32_MAX;
std::vector<TextureHandle> g_resolvedTextures;
std::vector<TextureUpload> g_textureUploads; std::vector<TextureUpload> g_textureUploads;
static ByteBuffer g_serializedPipelines{}; static ByteBuffer g_serializedPipelines{};
@ -212,7 +205,8 @@ static PipelineRef find_pipeline(ShaderType type, const PipelineConfig& config,
} }
static inline void push_command(CommandType type, const Command::Data& data) { static inline void push_command(CommandType type, const Command::Data& data) {
if (g_currentRenderPass == UINT32_MAX) { if (g_currentRenderPass == UINT32_MAX)
UNLIKELY {
Log.report(LOG_WARNING, FMT_STRING("Dropping command {}"), magic_enum::enum_name(type)); Log.report(LOG_WARNING, FMT_STRING("Dropping command {}"), magic_enum::enum_name(type));
return; return;
} }
@ -225,16 +219,13 @@ static inline void push_command(CommandType type, const Command::Data& data) {
}); });
} }
static inline Command& get_last_draw_command(ShaderType type) { static inline Command& get_last_draw_command(ShaderType type) {
if (g_currentRenderPass == UINT32_MAX) { CHECK(g_currentRenderPass != UINT32_MAX, "No last command");
Log.report(LOG_FATAL, FMT_STRING("No last command"));
unreachable();
}
auto& last = g_renderPasses[g_currentRenderPass].commands.back(); auto& last = g_renderPasses[g_currentRenderPass].commands.back();
if (last.type != CommandType::Draw || last.data.draw.type != type) { if (last.type != CommandType::Draw || last.data.draw.type != type)
Log.report(LOG_FATAL, FMT_STRING("Last command invalid: {} {}, expected {} {}"), magic_enum::enum_name(last.type), UNLIKELY {
FATAL("Last command invalid: {} {}, expected {} {}", magic_enum::enum_name(last.type),
magic_enum::enum_name(last.data.draw.type), magic_enum::enum_name(CommandType::Draw), magic_enum::enum_name(last.data.draw.type), magic_enum::enum_name(CommandType::Draw),
magic_enum::enum_name(type)); magic_enum::enum_name(type));
unreachable();
} }
return last; return last;
} }
@ -261,29 +252,13 @@ void set_scissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) noexcept {
} }
} }
static inline bool operator==(const wgpu::Extent3D& lhs, const wgpu::Extent3D& rhs) { void resolve_pass(TextureHandle texture, ClipRect rect, bool clear, Vec4<float> clearColor) {
return lhs.width == rhs.width && lhs.height == rhs.height && lhs.depthOrArrayLayers == rhs.depthOrArrayLayers; auto& currentPass = aurora::gfx::g_renderPasses[g_currentRenderPass];
} currentPass.resolveTarget = std::move(texture);
static inline bool operator!=(const wgpu::Extent3D& lhs, const wgpu::Extent3D& rhs) { return !(lhs == rhs); }
void resolve_color(const ClipRect& rect, uint32_t bind, GXTexFmt fmt, bool clear_depth) noexcept {
if (g_resolvedTextures.size() < bind + 1) {
g_resolvedTextures.resize(bind + 1);
}
const wgpu::Extent3D size{
.width = static_cast<uint32_t>(rect.width),
.height = static_cast<uint32_t>(rect.height),
.depthOrArrayLayers = 1,
};
if (!g_resolvedTextures[bind] || g_resolvedTextures[bind]->size != size) {
g_resolvedTextures[bind] = new_render_texture(rect.width, rect.height, fmt, "Resolved Texture");
}
auto& currentPass = g_renderPasses[g_currentRenderPass];
currentPass.resolveTarget = bind;
currentPass.resolveRect = rect; currentPass.resolveRect = rect;
auto& newPass = g_renderPasses.emplace_back(); auto& newPass = g_renderPasses.emplace_back();
newPass.clearColor = gx::g_gxState.clearColor; newPass.clearColor = clearColor;
newPass.clear = false; // TODO newPass.clear = clear;
++g_currentRenderPass; ++g_currentRenderPass;
} }
@ -298,14 +273,10 @@ void push_draw_command(stream::DrawData data) {
template <> template <>
void merge_draw_command(stream::DrawData data) { void merge_draw_command(stream::DrawData data) {
auto& last = get_last_draw_command(ShaderType::Stream).data.draw.stream; auto& last = get_last_draw_command(ShaderType::Stream).data.draw.stream;
if (last.vertRange.offset + last.vertRange.size != data.vertRange.offset) { CHECK(last.vertRange.offset + last.vertRange.size == data.vertRange.offset, "Invalid vertex merge range: {} -> {}",
Log.report(LOG_FATAL, FMT_STRING("Invalid merge range: {} -> {}"), last.vertRange.offset + last.vertRange.size, last.vertRange.offset + last.vertRange.size, data.vertRange.offset);
data.vertRange.offset); CHECK(last.indexRange.offset + last.indexRange.size == data.indexRange.offset, "Invalid index merge range: {} -> {}",
} last.indexRange.offset + last.indexRange.size, data.indexRange.offset);
if (last.indexRange.offset + last.indexRange.size != data.indexRange.offset) {
Log.report(LOG_FATAL, FMT_STRING("Invalid merge range: {} -> {}"), last.indexRange.offset + last.indexRange.size,
data.indexRange.offset);
}
last.vertRange.size += data.vertRange.size; last.vertRange.size += data.vertRange.size;
last.indexRange.size += data.indexRange.size; last.indexRange.size += data.indexRange.size;
last.indexCount += data.indexCount; last.indexCount += data.indexCount;
@ -343,10 +314,7 @@ static void pipeline_worker() {
// std::this_thread::sleep_for(std::chrono::milliseconds{1500}); // std::this_thread::sleep_for(std::chrono::milliseconds{1500});
{ {
std::scoped_lock lock{g_pipelineMutex}; std::scoped_lock lock{g_pipelineMutex};
if (!g_pipelines.try_emplace(cb.first, std::move(result)).second) { ASSERT(g_pipelines.try_emplace(cb.first, std::move(result)).second, "Duplicate pipeline {}", cb.first);
Log.report(LOG_FATAL, FMT_STRING("Duplicate pipeline {}"), cb.first);
unreachable();
}
g_queuedPipelines.pop_front(); g_queuedPipelines.pop_front();
hasMore = !g_queuedPipelines.empty(); hasMore = !g_queuedPipelines.empty();
} }
@ -473,7 +441,6 @@ void shutdown() {
gx::shutdown(); gx::shutdown();
g_resolvedTextures.clear();
g_textureUploads.clear(); g_textureUploads.clear();
g_cachedBindGroups.clear(); g_cachedBindGroups.clear();
g_cachedSamplers.clear(); g_cachedSamplers.clear();
@ -502,10 +469,8 @@ void map_staging_buffer() {
[](WGPUBufferMapAsyncStatus status, void* userdata) { [](WGPUBufferMapAsyncStatus status, void* userdata) {
if (status == WGPUBufferMapAsyncStatus_DestroyedBeforeCallback) { if (status == WGPUBufferMapAsyncStatus_DestroyedBeforeCallback) {
return; return;
} else if (status != WGPUBufferMapAsyncStatus_Success) {
Log.report(LOG_FATAL, FMT_STRING("Buffer mapping failed: {}"), status);
unreachable();
} }
ASSERT(status == WGPUBufferMapAsyncStatus_Success, "Buffer mapping failed: {}", static_cast<int>(status));
*static_cast<bool*>(userdata) = true; *static_cast<bool*>(userdata) = true;
}, },
&bufferMapped); &bufferMapped);
@ -585,10 +550,8 @@ void end_frame(const wgpu::CommandEncoder& cmd) {
void render(wgpu::CommandEncoder& cmd) { void render(wgpu::CommandEncoder& cmd) {
for (u32 i = 0; i < g_renderPasses.size(); ++i) { for (u32 i = 0; i < g_renderPasses.size(); ++i) {
const auto& passInfo = g_renderPasses[i]; const auto& passInfo = g_renderPasses[i];
bool finalPass = i == g_renderPasses.size() - 1; if (i == g_renderPasses.size() - 1) {
if (finalPass && passInfo.resolveTarget != UINT32_MAX) { ASSERT(!passInfo.resolveTarget, "Final render pass must not have resolve target");
Log.report(LOG_FATAL, FMT_STRING("Final render pass must not have resolve target"));
unreachable();
} }
const std::array attachments{ const std::array attachments{
wgpu::RenderPassColorAttachment{ wgpu::RenderPassColorAttachment{
@ -622,7 +585,7 @@ void render(wgpu::CommandEncoder& cmd) {
render_pass(pass, i); render_pass(pass, i);
pass.End(); pass.End();
if (passInfo.resolveTarget != UINT32_MAX) { if (passInfo.resolveTarget) {
wgpu::ImageCopyTexture src{ wgpu::ImageCopyTexture src{
.origin = .origin =
wgpu::Origin3D{ wgpu::Origin3D{
@ -635,9 +598,8 @@ void render(wgpu::CommandEncoder& cmd) {
} else { } else {
src.texture = webgpu::g_frameBuffer.texture; src.texture = webgpu::g_frameBuffer.texture;
} }
auto& target = g_resolvedTextures[passInfo.resolveTarget];
const wgpu::ImageCopyTexture dst{ const wgpu::ImageCopyTexture dst{
.texture = target->texture, .texture = passInfo.resolveTarget->texture,
}; };
const wgpu::Extent3D size{ const wgpu::Extent3D size{
.width = static_cast<uint32_t>(passInfo.resolveRect.width), .width = static_cast<uint32_t>(passInfo.resolveRect.width),
@ -804,10 +766,7 @@ const wgpu::BindGroup& find_bind_group(BindGroupRef id) {
return g_cachedBindGroups[id]; return g_cachedBindGroups[id];
#else #else
const auto it = g_cachedBindGroups.find(id); const auto it = g_cachedBindGroups.find(id);
if (it == g_cachedBindGroups.end()) { CHECK(it != g_cachedBindGroups.end(), "get_bind_group: failed to locate {:x}", id);
Log.report(LOG_FATAL, FMT_STRING("get_bind_group: failed to locate {}"), id);
unreachable();
}
return it->second; return it->second;
#endif #endif
} }

View File

@ -2,6 +2,7 @@
#include "../internal.hpp" #include "../internal.hpp"
#include <aurora/math.hpp>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <cstring> #include <cstring>
@ -136,9 +137,23 @@ struct Range {
uint32_t offset = 0; uint32_t offset = 0;
uint32_t size = 0; uint32_t size = 0;
inline bool operator==(const Range& rhs) { return offset == rhs.offset && size == rhs.size; } bool operator==(const Range& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
bool operator!=(const Range& rhs) const { return !(*this == rhs); }
}; };
struct ClipRect {
int32_t x;
int32_t y;
int32_t width;
int32_t height;
bool operator==(const ClipRect& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
bool operator!=(const ClipRect& rhs) const { return !(*this == rhs); }
};
struct TextureRef;
using TextureHandle = std::shared_ptr<TextureRef>;
enum class ShaderType { enum class ShaderType {
Stream, Stream,
Model, Model,
@ -152,6 +167,7 @@ void end_frame(const wgpu::CommandEncoder& cmd);
void render(wgpu::CommandEncoder& cmd); void render(wgpu::CommandEncoder& cmd);
void render_pass(const wgpu::RenderPassEncoder& pass, uint32_t idx); void render_pass(const wgpu::RenderPassEncoder& pass, uint32_t idx);
void map_staging_buffer(); void map_staging_buffer();
void resolve_pass(TextureHandle texture, ClipRect rect, bool clear, Vec4<float> clearColor);
Range push_verts(const uint8_t* data, size_t length); Range push_verts(const uint8_t* data, size_t length);
template <typename T> template <typename T>

View File

@ -25,6 +25,7 @@ const TextureBind& get_texture(GXTexMapID id) noexcept { return g_gxState.textur
static inline wgpu::BlendFactor to_blend_factor(GXBlendFactor fac, bool isDst) { static inline wgpu::BlendFactor to_blend_factor(GXBlendFactor fac, bool isDst) {
switch (fac) { switch (fac) {
DEFAULT_FATAL("invalid blend factor {}", static_cast<int>(fac));
case GX_BL_ZERO: case GX_BL_ZERO:
return wgpu::BlendFactor::Zero; return wgpu::BlendFactor::Zero;
case GX_BL_ONE: case GX_BL_ONE:
@ -49,14 +50,12 @@ static inline wgpu::BlendFactor to_blend_factor(GXBlendFactor fac, bool isDst) {
return wgpu::BlendFactor::DstAlpha; return wgpu::BlendFactor::DstAlpha;
case GX_BL_INVDSTALPHA: case GX_BL_INVDSTALPHA:
return wgpu::BlendFactor::OneMinusDstAlpha; return wgpu::BlendFactor::OneMinusDstAlpha;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid blend factor {}"), fac);
unreachable();
} }
} }
static inline wgpu::CompareFunction to_compare_function(GXCompare func) { static inline wgpu::CompareFunction to_compare_function(GXCompare func) {
switch (func) { switch (func) {
DEFAULT_FATAL("invalid depth fn {}", static_cast<int>(func));
case GX_NEVER: case GX_NEVER:
return wgpu::CompareFunction::Never; return wgpu::CompareFunction::Never;
case GX_LESS: case GX_LESS:
@ -73,9 +72,6 @@ static inline wgpu::CompareFunction to_compare_function(GXCompare func) {
return wgpu::CompareFunction::GreaterEqual; return wgpu::CompareFunction::GreaterEqual;
case GX_ALWAYS: case GX_ALWAYS:
return wgpu::CompareFunction::Always; return wgpu::CompareFunction::Always;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid depth fn {}"), func);
unreachable();
} }
} }
@ -83,6 +79,7 @@ static inline wgpu::BlendState to_blend_state(GXBlendMode mode, GXBlendFactor sr
GXLogicOp op, u32 dstAlpha) { GXLogicOp op, u32 dstAlpha) {
wgpu::BlendComponent colorBlendComponent; wgpu::BlendComponent colorBlendComponent;
switch (mode) { switch (mode) {
DEFAULT_FATAL("unsupported blend mode {}", static_cast<int>(mode));
case GX_BM_NONE: case GX_BM_NONE:
colorBlendComponent = { colorBlendComponent = {
.operation = wgpu::BlendOperation::Add, .operation = wgpu::BlendOperation::Add,
@ -106,6 +103,7 @@ static inline wgpu::BlendState to_blend_state(GXBlendMode mode, GXBlendFactor sr
break; break;
case GX_BM_LOGIC: case GX_BM_LOGIC:
switch (op) { switch (op) {
DEFAULT_FATAL("unsupported logic op {}", static_cast<int>(op));
case GX_LO_CLEAR: case GX_LO_CLEAR:
colorBlendComponent = { colorBlendComponent = {
.operation = wgpu::BlendOperation::Add, .operation = wgpu::BlendOperation::Add,
@ -127,14 +125,8 @@ static inline wgpu::BlendState to_blend_state(GXBlendMode mode, GXBlendFactor sr
.dstFactor = wgpu::BlendFactor::One, .dstFactor = wgpu::BlendFactor::One,
}; };
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("unsupported logic op {}"), op);
unreachable();
} }
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("unsupported blend mode {}"), mode);
unreachable();
} }
wgpu::BlendComponent alphaBlendComponent{ wgpu::BlendComponent alphaBlendComponent{
.operation = wgpu::BlendOperation::Add, .operation = wgpu::BlendOperation::Add,
@ -168,17 +160,16 @@ static inline wgpu::ColorWriteMask to_write_mask(bool colorUpdate, bool alphaUpd
static inline wgpu::PrimitiveState to_primitive_state(GXPrimitive gx_prim, GXCullMode gx_cullMode) { static inline wgpu::PrimitiveState to_primitive_state(GXPrimitive gx_prim, GXCullMode gx_cullMode) {
wgpu::PrimitiveTopology primitive = wgpu::PrimitiveTopology::TriangleList; wgpu::PrimitiveTopology primitive = wgpu::PrimitiveTopology::TriangleList;
switch (gx_prim) { switch (gx_prim) {
DEFAULT_FATAL("unsupported primitive type {}", static_cast<int>(gx_prim));
case GX_TRIANGLES: case GX_TRIANGLES:
break; break;
case GX_TRIANGLESTRIP: case GX_TRIANGLESTRIP:
primitive = wgpu::PrimitiveTopology::TriangleStrip; primitive = wgpu::PrimitiveTopology::TriangleStrip;
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("Unsupported primitive type {}"), gx_prim);
unreachable();
} }
wgpu::CullMode cullMode = wgpu::CullMode::None; wgpu::CullMode cullMode = wgpu::CullMode::None;
switch (gx_cullMode) { switch (gx_cullMode) {
DEFAULT_FATAL("unsupported cull mode {}", static_cast<int>(gx_cullMode));
case GX_CULL_FRONT: case GX_CULL_FRONT:
cullMode = wgpu::CullMode::Front; cullMode = wgpu::CullMode::Front;
break; break;
@ -187,9 +178,6 @@ static inline wgpu::PrimitiveState to_primitive_state(GXPrimitive gx_prim, GXCul
break; break;
case GX_CULL_NONE: case GX_CULL_NONE:
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("Unsupported cull mode {}"), gx_cullMode);
unreachable();
} }
return { return {
.topology = primitive, .topology = primitive,
@ -404,6 +392,7 @@ Range build_uniform(const ShaderInfo& info) noexcept {
} }
const auto& state = g_gxState; const auto& state = g_gxState;
switch (info.texMtxTypes[i]) { switch (info.texMtxTypes[i]) {
DEFAULT_FATAL("unhandled tex mtx type {}", static_cast<int>(info.texMtxTypes[i]));
case GX_TG_MTX2x4: case GX_TG_MTX2x4:
if (std::holds_alternative<Mat4x2<float>>(state.texMtxs[i])) { if (std::holds_alternative<Mat4x2<float>>(state.texMtxs[i])) {
buf.append(&std::get<Mat4x2<float>>(state.texMtxs[i]), 32); buf.append(&std::get<Mat4x2<float>>(state.texMtxs[i]), 32);
@ -416,23 +405,16 @@ Range build_uniform(const ShaderInfo& info) noexcept {
{0.f, 0.f}, {0.f, 0.f},
}; };
buf.append(&mtx, 32); buf.append(&mtx, 32);
} else { } else
Log.report(LOG_FATAL, FMT_STRING("expected 2x4 mtx in idx {}"), i); UNLIKELY FATAL("expected 2x4 mtx in idx {}", i);
unreachable();
}
break; break;
case GX_TG_MTX3x4: case GX_TG_MTX3x4:
if (std::holds_alternative<Mat4x4<float>>(g_gxState.texMtxs[i])) { if (std::holds_alternative<Mat4x4<float>>(g_gxState.texMtxs[i])) {
const auto& mat = std::get<Mat4x4<float>>(g_gxState.texMtxs[i]); const auto& mat = std::get<Mat4x4<float>>(g_gxState.texMtxs[i]);
buf.append(&mat, 64); buf.append(&mat, 64);
} else { } else
Log.report(LOG_FATAL, FMT_STRING("expected 3x4 mtx in idx {}"), i); UNLIKELY FATAL("expected 3x4 mtx in idx {}", i);
buf.append(&Mat4x4_Identity, 64);
}
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("unhandled tex mtx type {}"), info.texMtxTypes[i]);
unreachable();
} }
} }
for (int i = 0; i < info.usesPTTexMtx.size(); ++i) { for (int i = 0; i < info.usesPTTexMtx.size(); ++i) {
@ -465,10 +447,7 @@ Range build_uniform(const ShaderInfo& info) noexcept {
continue; continue;
} }
const auto& tex = get_texture(static_cast<GXTexMapID>(i)); const auto& tex = get_texture(static_cast<GXTexMapID>(i));
if (!tex) { CHECK(tex, "unbound texture {}", i);
Log.report(LOG_FATAL, FMT_STRING("unbound texture {}"), i);
unreachable();
}
buf.append(&tex.texObj.lodBias, 4); buf.append(&tex.texObj.lodBias, 4);
} }
g_gxState.stateDirty = false; g_gxState.stateDirty = false;
@ -512,10 +491,7 @@ GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& confi
continue; continue;
} }
const auto& tex = g_gxState.textures[i]; const auto& tex = g_gxState.textures[i];
if (!tex) { CHECK(tex, "unbound texture {}", i);
Log.report(LOG_FATAL, FMT_STRING("unbound texture {}"), i);
unreachable();
}
samplerEntries[samplerCount] = { samplerEntries[samplerCount] = {
.binding = samplerCount, .binding = samplerCount,
.sampler = sampler_ref(tex.get_descriptor()), .sampler = sampler_ref(tex.get_descriptor()),
@ -530,13 +506,8 @@ GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& confi
const auto& texConfig = config.textureConfig[i]; const auto& texConfig = config.textureConfig[i];
if (is_palette_format(texConfig.loadFmt)) { if (is_palette_format(texConfig.loadFmt)) {
u32 tlut = tex.texObj.tlut; u32 tlut = tex.texObj.tlut;
if (tlut < GX_TLUT0 || tlut > GX_TLUT7) { CHECK(tlut >= GX_TLUT0 && tlut <= GX_BIGTLUT3, "tlut out of bounds {}", tlut);
Log.report(LOG_FATAL, FMT_STRING("tlut out of bounds {}"), tlut); CHECK(g_gxState.tluts[tlut].ref, "tlut unbound {}", tlut);
unreachable();
} else if (!g_gxState.tluts[tlut].ref) {
Log.report(LOG_FATAL, FMT_STRING("tlut unbound {}"), tlut);
unreachable();
}
textureEntries[textureCount] = { textureEntries[textureCount] = {
.binding = textureCount, .binding = textureCount,
.textureView = g_gxState.tluts[tlut].ref->view, .textureView = g_gxState.tluts[tlut].ref->view,
@ -702,24 +673,24 @@ void shutdown() noexcept {
item.ref.reset(); item.ref.reset();
} }
g_gxCachedShaders.clear(); g_gxCachedShaders.clear();
g_gxState.copyTextures.clear();
} }
} // namespace gx } // namespace gx
static wgpu::AddressMode wgpu_address_mode(GXTexWrapMode mode) { static wgpu::AddressMode wgpu_address_mode(GXTexWrapMode mode) {
switch (mode) { switch (mode) {
DEFAULT_FATAL("invalid wrap mode {}", static_cast<int>(mode));
case GX_CLAMP: case GX_CLAMP:
return wgpu::AddressMode::ClampToEdge; return wgpu::AddressMode::ClampToEdge;
case GX_REPEAT: case GX_REPEAT:
return wgpu::AddressMode::Repeat; return wgpu::AddressMode::Repeat;
case GX_MIRROR: case GX_MIRROR:
return wgpu::AddressMode::MirrorRepeat; return wgpu::AddressMode::MirrorRepeat;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid wrap mode {}"), mode);
unreachable();
} }
} }
static std::pair<wgpu::FilterMode, wgpu::FilterMode> wgpu_filter_mode(GXTexFilter filter) { static std::pair<wgpu::FilterMode, wgpu::FilterMode> wgpu_filter_mode(GXTexFilter filter) {
switch (filter) { switch (filter) {
DEFAULT_FATAL("invalid filter mode {}", static_cast<int>(filter));
case GX_NEAR: case GX_NEAR:
return {wgpu::FilterMode::Nearest, wgpu::FilterMode::Linear}; return {wgpu::FilterMode::Nearest, wgpu::FilterMode::Linear};
case GX_LINEAR: case GX_LINEAR:
@ -732,22 +703,17 @@ static std::pair<wgpu::FilterMode, wgpu::FilterMode> wgpu_filter_mode(GXTexFilte
return {wgpu::FilterMode::Nearest, wgpu::FilterMode::Linear}; return {wgpu::FilterMode::Nearest, wgpu::FilterMode::Linear};
case GX_LIN_MIP_LIN: case GX_LIN_MIP_LIN:
return {wgpu::FilterMode::Linear, wgpu::FilterMode::Linear}; return {wgpu::FilterMode::Linear, wgpu::FilterMode::Linear};
default:
Log.report(LOG_FATAL, FMT_STRING("invalid filter mode {}"), filter);
unreachable();
} }
} }
static u16 wgpu_aniso(GXAnisotropy aniso) { static u16 wgpu_aniso(GXAnisotropy aniso) {
switch (aniso) { switch (aniso) {
DEFAULT_FATAL("invalid aniso {}", static_cast<int>(aniso));
case GX_ANISO_1: case GX_ANISO_1:
return 1; return 1;
case GX_ANISO_2: case GX_ANISO_2:
return std::max<u16>(webgpu::g_graphicsConfig.textureAnisotropy / 2, 1); return std::max<u16>(webgpu::g_graphicsConfig.textureAnisotropy / 2, 1);
case GX_ANISO_4: case GX_ANISO_4:
return std::max<u16>(webgpu::g_graphicsConfig.textureAnisotropy, 1); return std::max<u16>(webgpu::g_graphicsConfig.textureAnisotropy, 1);
default:
Log.report(LOG_FATAL, FMT_STRING("invalid aniso mode {}"), aniso);
unreachable();
} }
} }
wgpu::SamplerDescriptor TextureBind::get_descriptor() const noexcept { wgpu::SamplerDescriptor TextureBind::get_descriptor() const noexcept {

View File

@ -6,6 +6,7 @@
#include "../internal.hpp" #include "../internal.hpp"
#include "texture.hpp" #include "texture.hpp"
#include <absl/container/flat_hash_map.h>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <variant> #include <variant>
@ -46,6 +47,7 @@ constexpr float GX_LARGE_NUMBER = -1048576.0f;
namespace aurora::gfx::gx { namespace aurora::gfx::gx {
constexpr u32 MaxTextures = GX_MAX_TEXMAP; constexpr u32 MaxTextures = GX_MAX_TEXMAP;
constexpr u32 MaxTluts = 20;
constexpr u32 MaxTevStages = GX_MAX_TEVSTAGE; constexpr u32 MaxTevStages = GX_MAX_TEVSTAGE;
constexpr u32 MaxColorChannels = 4; constexpr u32 MaxColorChannels = 4;
constexpr u32 MaxTevRegs = 4; // TEVPREV, TEVREG0-2 constexpr u32 MaxTevRegs = 4; // TEVPREV, TEVREG0-2
@ -151,6 +153,7 @@ struct TcgConfig {
u8 _p3 = 0; u8 _p3 = 0;
bool operator==(const TcgConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } bool operator==(const TcgConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
bool operator!=(const TcgConfig& rhs) const { return !(*this == rhs); }
}; };
static_assert(std::has_unique_object_representations_v<TcgConfig>); static_assert(std::has_unique_object_representations_v<TcgConfig>);
struct FogState { struct FogState {
@ -165,6 +168,7 @@ struct FogState {
return type == rhs.type && startZ == rhs.startZ && endZ == rhs.endZ && nearZ == rhs.nearZ && farZ == rhs.farZ && return type == rhs.type && startZ == rhs.startZ && endZ == rhs.endZ && nearZ == rhs.nearZ && farZ == rhs.farZ &&
color == rhs.color; color == rhs.color;
} }
bool operator!=(const FogState& rhs) const { return !(*this == rhs); }
}; };
struct TevSwap { struct TevSwap {
GXTevColorChan red = GX_CH_RED; GXTevColorChan red = GX_CH_RED;
@ -173,6 +177,7 @@ struct TevSwap {
GXTevColorChan alpha = GX_CH_ALPHA; GXTevColorChan alpha = GX_CH_ALPHA;
bool operator==(const TevSwap& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } bool operator==(const TevSwap& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
bool operator!=(const TevSwap& rhs) const { return !(*this == rhs); }
explicit operator bool() const { return !(*this == TevSwap{}); } explicit operator bool() const { return !(*this == TevSwap{}); }
}; };
static_assert(std::has_unique_object_representations_v<TevSwap>); static_assert(std::has_unique_object_representations_v<TevSwap>);
@ -184,6 +189,7 @@ struct AlphaCompare {
u32 ref1; u32 ref1;
bool operator==(const AlphaCompare& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } bool operator==(const AlphaCompare& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
bool operator!=(const AlphaCompare& rhs) const { return !(*this == rhs); }
explicit operator bool() const { return comp0 != GX_ALWAYS || comp1 != GX_ALWAYS; } explicit operator bool() const { return comp0 != GX_ALWAYS || comp1 != GX_ALWAYS; }
}; };
static_assert(std::has_unique_object_representations_v<AlphaCompare>); static_assert(std::has_unique_object_representations_v<AlphaCompare>);
@ -192,6 +198,7 @@ struct IndTexMtxInfo {
s8 scaleExp; s8 scaleExp;
bool operator==(const IndTexMtxInfo& rhs) const { return mtx == rhs.mtx && scaleExp == rhs.scaleExp; } bool operator==(const IndTexMtxInfo& rhs) const { return mtx == rhs.mtx && scaleExp == rhs.scaleExp; }
bool operator!=(const IndTexMtxInfo& rhs) const { return !(*this == rhs); }
}; };
struct VtxAttrFmt { struct VtxAttrFmt {
GXCompCnt cnt; GXCompCnt cnt;
@ -216,6 +223,7 @@ struct Light {
bool operator==(const Light& rhs) const { bool operator==(const Light& rhs) const {
return pos == rhs.pos && dir == rhs.dir && color == rhs.color && cosAtt == rhs.cosAtt && distAtt == rhs.distAtt; return pos == rhs.pos && dir == rhs.dir && color == rhs.color && cosAtt == rhs.cosAtt && distAtt == rhs.distAtt;
} }
bool operator!=(const Light& rhs) const { return !(*this == rhs); }
}; };
static_assert(sizeof(Light) == 80); static_assert(sizeof(Light) == 80);
struct AttrArray { struct AttrArray {
@ -227,6 +235,7 @@ struct AttrArray {
inline bool operator==(const AttrArray& lhs, const AttrArray& rhs) { inline bool operator==(const AttrArray& lhs, const AttrArray& rhs) {
return lhs.data == rhs.data && lhs.size == rhs.size && lhs.stride == rhs.stride; return lhs.data == rhs.data && lhs.size == rhs.size && lhs.stride == rhs.stride;
} }
inline bool operator!=(const AttrArray& lhs, const AttrArray& rhs) { return !(lhs == rhs); }
struct GXState { struct GXState {
std::array<PnMtx, MaxPnMtx> pnMtx; std::array<PnMtx, MaxPnMtx> pnMtx;
@ -251,7 +260,7 @@ struct GXState {
std::array<Light, GX::MaxLights> lights; std::array<Light, GX::MaxLights> lights;
std::array<TevStage, MaxTevStages> tevStages; std::array<TevStage, MaxTevStages> tevStages;
std::array<TextureBind, MaxTextures> textures; std::array<TextureBind, MaxTextures> textures;
std::array<GXTlutObj_, MaxTextures> tluts; std::array<GXTlutObj_, MaxTluts> tluts;
std::array<TexMtxVariant, MaxTexMtx> texMtxs; std::array<TexMtxVariant, MaxTexMtx> texMtxs;
std::array<Mat4x4<float>, MaxPTTexMtx> ptTexMtxs; std::array<Mat4x4<float>, MaxPTTexMtx> ptTexMtxs;
std::array<TcgConfig, MaxTexCoord> tcgs; std::array<TcgConfig, MaxTexCoord> tcgs;
@ -266,6 +275,9 @@ struct GXState {
std::array<IndStage, MaxIndStages> indStages; std::array<IndStage, MaxIndStages> indStages;
std::array<IndTexMtxInfo, MaxIndTexMtxs> indTexMtxs; std::array<IndTexMtxInfo, MaxIndTexMtxs> indTexMtxs;
std::array<AttrArray, GX_VA_MAX_ATTR> arrays; std::array<AttrArray, GX_VA_MAX_ATTR> arrays;
ClipRect texCopySrc;
GXTexFmt texCopyFmt;
absl::flat_hash_map<void*, TextureHandle> copyTextures;
bool depthCompare = true; bool depthCompare = true;
bool depthUpdate = true; bool depthUpdate = true;
bool colorUpdate = true; bool colorUpdate = true;

View File

@ -64,12 +64,8 @@ static void color_arg_reg_info(GXTevColorArg arg, const TevStage& stage, ShaderI
break; break;
case GX_CC_TEXC: case GX_CC_TEXC:
case GX_CC_TEXA: case GX_CC_TEXA:
if (stage.texCoordId == GX_TEXCOORD_NULL) { CHECK(stage.texCoordId != GX_TEXCOORD_NULL, "tex coord not bound");
Log.report(LOG_FATAL, FMT_STRING("texCoord not bound")); CHECK(stage.texMapId != GX_TEXMAP_NULL, "tex map not bound");
}
if (stage.texMapId == GX_TEXMAP_NULL) {
Log.report(LOG_FATAL, FMT_STRING("texMap not bound"));
}
info.sampledTexCoords.set(stage.texCoordId); info.sampledTexCoords.set(stage.texCoordId);
info.sampledTextures.set(stage.texMapId); info.sampledTextures.set(stage.texMapId);
break; break;
@ -139,6 +135,7 @@ static bool formatHasAlpha(u32 format) {
static std::string color_arg_reg(GXTevColorArg arg, size_t stageIdx, const ShaderConfig& config, static std::string color_arg_reg(GXTevColorArg arg, size_t stageIdx, const ShaderConfig& config,
const TevStage& stage) { const TevStage& stage) {
switch (arg) { switch (arg) {
DEFAULT_FATAL("invalid color arg {}", static_cast<int>(arg));
case GX_CC_CPREV: case GX_CC_CPREV:
return "prev.rgb"; return "prev.rgb";
case GX_CC_APREV: case GX_CC_APREV:
@ -156,53 +153,39 @@ static std::string color_arg_reg(GXTevColorArg arg, size_t stageIdx, const Shade
case GX_CC_A2: case GX_CC_A2:
return "vec3<f32>(tevreg2.a)"; return "vec3<f32>(tevreg2.a)";
case GX_CC_TEXC: { case GX_CC_TEXC: {
if (stage.texMapId == GX_TEXMAP_NULL) { CHECK(stage.texMapId != GX_TEXMAP_NULL, "unmapped texture for stage {}", stageIdx);
Log.report(LOG_FATAL, FMT_STRING("unmapped texture for stage {}"), stageIdx); CHECK(stage.texMapId >= GX_TEXMAP0 && stage.texMapId <= GX_TEXMAP7, "invalid texture {} for stage {}",
unreachable(); static_cast<int>(stage.texMapId), stageIdx);
} else if (stage.texMapId < GX_TEXMAP0 || stage.texMapId > GX_TEXMAP7) {
Log.report(LOG_FATAL, FMT_STRING("invalid texture {} for stage {}"), stage.texMapId, stageIdx);
unreachable();
}
const auto& swap = config.tevSwapTable[stage.tevSwapTex]; const auto& swap = config.tevSwapTable[stage.tevSwapTex];
return fmt::format(FMT_STRING("sampled{}.{}{}{}"), stageIdx, chan_comp(swap.red), chan_comp(swap.green), return fmt::format(FMT_STRING("sampled{}.{}{}{}"), stageIdx, chan_comp(swap.red), chan_comp(swap.green),
chan_comp(swap.blue)); chan_comp(swap.blue));
} }
case GX_CC_TEXA: { case GX_CC_TEXA: {
if (stage.texMapId == GX_TEXMAP_NULL) { CHECK(stage.texMapId != GX_TEXMAP_NULL, "unmapped texture for stage {}", stageIdx);
Log.report(LOG_FATAL, FMT_STRING("unmapped texture for stage {}"), stageIdx); CHECK(stage.texMapId >= GX_TEXMAP0 && stage.texMapId <= GX_TEXMAP7, "invalid texture {} for stage {}",
unreachable(); static_cast<int>(stage.texMapId), stageIdx);
} else if (stage.texMapId < GX_TEXMAP0 || stage.texMapId > GX_TEXMAP7) {
Log.report(LOG_FATAL, FMT_STRING("invalid texture {} for stage {}"), stage.texMapId, stageIdx);
unreachable();
}
const auto& swap = config.tevSwapTable[stage.tevSwapTex]; const auto& swap = config.tevSwapTable[stage.tevSwapTex];
return fmt::format(FMT_STRING("vec3<f32>(sampled{}.{})"), stageIdx, chan_comp(swap.alpha)); return fmt::format(FMT_STRING("vec3<f32>(sampled{}.{})"), stageIdx, chan_comp(swap.alpha));
} }
case GX_CC_RASC: { case GX_CC_RASC: {
if (stage.channelId == GX_COLOR_NULL) { CHECK(stage.channelId != GX_COLOR_NULL, "unmapped color channel for stage {}", stageIdx);
Log.report(LOG_FATAL, FMT_STRING("unmapped color channel for stage {}"), stageIdx); if (stage.channelId == GX_COLOR_ZERO) {
unreachable();
} else if (stage.channelId == GX_COLOR_ZERO) {
return "vec3<f32>(0.0)"; return "vec3<f32>(0.0)";
} else if (stage.channelId < GX_COLOR0A0 || stage.channelId > GX_COLOR1A1) {
Log.report(LOG_FATAL, FMT_STRING("invalid color channel {} for stage {}"), stage.channelId, stageIdx);
unreachable();
} }
CHECK(stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1, "invalid color channel {} for stage {}",
static_cast<int>(stage.channelId), stageIdx);
u32 idx = stage.channelId - GX_COLOR0A0; u32 idx = stage.channelId - GX_COLOR0A0;
const auto& swap = config.tevSwapTable[stage.tevSwapRas]; const auto& swap = config.tevSwapTable[stage.tevSwapRas];
return fmt::format(FMT_STRING("rast{}.{}{}{}"), idx, chan_comp(swap.red), chan_comp(swap.green), return fmt::format(FMT_STRING("rast{}.{}{}{}"), idx, chan_comp(swap.red), chan_comp(swap.green),
chan_comp(swap.blue)); chan_comp(swap.blue));
} }
case GX_CC_RASA: { case GX_CC_RASA: {
if (stage.channelId == GX_COLOR_NULL) { CHECK(stage.channelId != GX_COLOR_NULL, "unmapped color channel for stage {}", stageIdx);
Log.report(LOG_FATAL, FMT_STRING("unmapped color channel for stage {}"), stageIdx); if (stage.channelId == GX_COLOR_ZERO) {
unreachable();
} else if (stage.channelId == GX_COLOR_ZERO) {
return "vec3<f32>(0.0)"; return "vec3<f32>(0.0)";
} else if (stage.channelId < GX_COLOR0A0 || stage.channelId > GX_COLOR1A1) {
Log.report(LOG_FATAL, FMT_STRING("invalid color channel {} for stage {}"), stage.channelId, stageIdx);
unreachable();
} }
CHECK(stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1, "invalid color channel {} for stage {}",
static_cast<int>(stage.channelId), stageIdx);
u32 idx = stage.channelId - GX_COLOR0A0; u32 idx = stage.channelId - GX_COLOR0A0;
const auto& swap = config.tevSwapTable[stage.tevSwapRas]; const auto& swap = config.tevSwapTable[stage.tevSwapRas];
return fmt::format(FMT_STRING("vec3<f32>(rast{}.{})"), idx, chan_comp(swap.alpha)); return fmt::format(FMT_STRING("vec3<f32>(rast{}.{})"), idx, chan_comp(swap.alpha));
@ -213,6 +196,7 @@ static std::string color_arg_reg(GXTevColorArg arg, size_t stageIdx, const Shade
return "vec3<f32>(0.5)"; return "vec3<f32>(0.5)";
case GX_CC_KONST: { case GX_CC_KONST: {
switch (stage.kcSel) { switch (stage.kcSel) {
DEFAULT_FATAL("invalid kcSel {}", static_cast<int>(stage.kcSel));
case GX_TEV_KCSEL_8_8: case GX_TEV_KCSEL_8_8:
return "vec3<f32>(1.0)"; return "vec3<f32>(1.0)";
case GX_TEV_KCSEL_7_8: case GX_TEV_KCSEL_7_8:
@ -269,16 +253,10 @@ static std::string color_arg_reg(GXTevColorArg arg, size_t stageIdx, const Shade
return "vec3<f32>(ubuf.kcolor2.a)"; return "vec3<f32>(ubuf.kcolor2.a)";
case GX_TEV_KCSEL_K3_A: case GX_TEV_KCSEL_K3_A:
return "vec3<f32>(ubuf.kcolor3.a)"; return "vec3<f32>(ubuf.kcolor3.a)";
default:
Log.report(LOG_FATAL, FMT_STRING("invalid kcSel {}"), stage.kcSel);
unreachable();
} }
} }
case GX_CC_ZERO: case GX_CC_ZERO:
return "vec3<f32>(0.0)"; return "vec3<f32>(0.0)";
default:
Log.report(LOG_FATAL, FMT_STRING("invalid color arg {}"), arg);
unreachable();
} }
} }
@ -305,12 +283,8 @@ static void alpha_arg_reg_info(GXTevAlphaArg arg, const TevStage& stage, ShaderI
} }
break; break;
case GX_CA_TEXA: case GX_CA_TEXA:
if (stage.texCoordId == GX_TEXCOORD_NULL) { CHECK(stage.texCoordId != GX_TEXCOORD_NULL, "tex coord not bound");
Log.report(LOG_FATAL, FMT_STRING("texCoord not bound")); CHECK(stage.texMapId != GX_TEXMAP_NULL, "tex map not bound");
}
if (stage.texMapId == GX_TEXMAP_NULL) {
Log.report(LOG_FATAL, FMT_STRING("texMap not bound"));
}
info.sampledTexCoords.set(stage.texCoordId); info.sampledTexCoords.set(stage.texCoordId);
info.sampledTextures.set(stage.texMapId); info.sampledTextures.set(stage.texMapId);
break; break;
@ -357,6 +331,7 @@ static void alpha_arg_reg_info(GXTevAlphaArg arg, const TevStage& stage, ShaderI
static std::string alpha_arg_reg(GXTevAlphaArg arg, size_t stageIdx, const ShaderConfig& config, static std::string alpha_arg_reg(GXTevAlphaArg arg, size_t stageIdx, const ShaderConfig& config,
const TevStage& stage) { const TevStage& stage) {
switch (arg) { switch (arg) {
DEFAULT_FATAL("invalid alpha arg {}", static_cast<int>(arg));
case GX_CA_APREV: case GX_CA_APREV:
return "prev.a"; return "prev.a";
case GX_CA_A0: case GX_CA_A0:
@ -366,32 +341,26 @@ static std::string alpha_arg_reg(GXTevAlphaArg arg, size_t stageIdx, const Shade
case GX_CA_A2: case GX_CA_A2:
return "tevreg2.a"; return "tevreg2.a";
case GX_CA_TEXA: { case GX_CA_TEXA: {
if (stage.texMapId == GX_TEXMAP_NULL) { CHECK(stage.texMapId != GX_TEXMAP_NULL, "unmapped texture for stage {}", stageIdx);
Log.report(LOG_FATAL, FMT_STRING("unmapped texture for stage {}"), stageIdx); CHECK(stage.texMapId >= GX_TEXMAP0 && stage.texMapId <= GX_TEXMAP7, "invalid texture {} for stage {}",
unreachable(); static_cast<int>(stage.texMapId), stageIdx);
} else if (stage.texMapId < GX_TEXMAP0 || stage.texMapId > GX_TEXMAP7) {
Log.report(LOG_FATAL, FMT_STRING("invalid texture {} for stage {}"), stage.texMapId, stageIdx);
unreachable();
}
const auto& swap = config.tevSwapTable[stage.tevSwapTex]; const auto& swap = config.tevSwapTable[stage.tevSwapTex];
return fmt::format(FMT_STRING("sampled{}.{}"), stageIdx, chan_comp(swap.alpha)); return fmt::format(FMT_STRING("sampled{}.{}"), stageIdx, chan_comp(swap.alpha));
} }
case GX_CA_RASA: { case GX_CA_RASA: {
if (stage.channelId == GX_COLOR_NULL) { CHECK(stage.channelId != GX_COLOR_NULL, "unmapped color channel for stage {}", stageIdx);
Log.report(LOG_FATAL, FMT_STRING("unmapped color channel for stage {}"), stageIdx); if (stage.channelId == GX_COLOR_ZERO) {
unreachable();
} else if (stage.channelId == GX_COLOR_ZERO) {
return "0.0"; return "0.0";
} else if (stage.channelId < GX_COLOR0A0 || stage.channelId > GX_COLOR1A1) {
Log.report(LOG_FATAL, FMT_STRING("invalid color channel {} for stage {}"), stage.channelId, stageIdx);
unreachable();
} }
CHECK(stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1, "invalid color channel {} for stage {}",
static_cast<int>(stage.channelId), stageIdx);
u32 idx = stage.channelId - GX_COLOR0A0; u32 idx = stage.channelId - GX_COLOR0A0;
const auto& swap = config.tevSwapTable[stage.tevSwapRas]; const auto& swap = config.tevSwapTable[stage.tevSwapRas];
return fmt::format(FMT_STRING("rast{}.{}"), idx, chan_comp(swap.alpha)); return fmt::format(FMT_STRING("rast{}.{}"), idx, chan_comp(swap.alpha));
} }
case GX_CA_KONST: { case GX_CA_KONST: {
switch (stage.kaSel) { switch (stage.kaSel) {
DEFAULT_FATAL("invalid kaSel {}", static_cast<int>(stage.kaSel));
case GX_TEV_KASEL_8_8: case GX_TEV_KASEL_8_8:
return "1.0"; return "1.0";
case GX_TEV_KASEL_7_8: case GX_TEV_KASEL_7_8:
@ -440,48 +409,39 @@ static std::string alpha_arg_reg(GXTevAlphaArg arg, size_t stageIdx, const Shade
return "ubuf.kcolor2.a"; return "ubuf.kcolor2.a";
case GX_TEV_KASEL_K3_A: case GX_TEV_KASEL_K3_A:
return "ubuf.kcolor3.a"; return "ubuf.kcolor3.a";
default:
Log.report(LOG_FATAL, FMT_STRING("invalid kaSel {}"), stage.kaSel);
unreachable();
} }
} }
case GX_CA_ZERO: case GX_CA_ZERO:
return "0.0"; return "0.0";
default:
Log.report(LOG_FATAL, FMT_STRING("invalid alpha arg {}"), arg);
unreachable();
} }
} }
static std::string_view tev_op(GXTevOp op) { static std::string_view tev_op(GXTevOp op) {
switch (op) { switch (op) {
DEFAULT_FATAL("unimplemented tev op {}", static_cast<int>(op));
case GX_TEV_ADD: case GX_TEV_ADD:
return ""sv; return ""sv;
case GX_TEV_SUB: case GX_TEV_SUB:
return "-"sv; return "-"sv;
default:
Log.report(LOG_FATAL, FMT_STRING("TODO {}"), op);
unreachable();
} }
} }
static std::string_view tev_bias(GXTevBias bias) { static std::string_view tev_bias(GXTevBias bias) {
switch (bias) { switch (bias) {
DEFAULT_FATAL("invalid tev bias {}", static_cast<int>(bias));
case GX_TB_ZERO: case GX_TB_ZERO:
return ""sv; return ""sv;
case GX_TB_ADDHALF: case GX_TB_ADDHALF:
return " + 0.5"sv; return " + 0.5"sv;
case GX_TB_SUBHALF: case GX_TB_SUBHALF:
return " - 0.5"sv; return " - 0.5"sv;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid bias {}"), bias);
unreachable();
} }
} }
static std::string alpha_compare(GXCompare comp, u8 ref, bool& valid) { static std::string alpha_compare(GXCompare comp, u8 ref, bool& valid) {
const float fref = ref / 255.f; const float fref = ref / 255.f;
switch (comp) { switch (comp) {
DEFAULT_FATAL("invalid alpha comp {}", static_cast<int>(comp));
case GX_NEVER: case GX_NEVER:
return "false"s; return "false"s;
case GX_LESS: case GX_LESS:
@ -499,14 +459,12 @@ static std::string alpha_compare(GXCompare comp, u8 ref, bool& valid) {
case GX_ALWAYS: case GX_ALWAYS:
valid = false; valid = false;
return "true"s; return "true"s;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid compare {}"), comp);
unreachable();
} }
} }
static std::string_view tev_scale(GXTevScale scale) { static std::string_view tev_scale(GXTevScale scale) {
switch (scale) { switch (scale) {
DEFAULT_FATAL("invalid tev scale {}", static_cast<int>(scale));
case GX_CS_SCALE_1: case GX_CS_SCALE_1:
return ""sv; return ""sv;
case GX_CS_SCALE_2: case GX_CS_SCALE_2:
@ -515,9 +473,6 @@ static std::string_view tev_scale(GXTevScale scale) {
return " * 4.0"sv; return " * 4.0"sv;
case GX_CS_DIVIDE_2: case GX_CS_DIVIDE_2:
return " / 2.0"sv; return " / 2.0"sv;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid scale {}"), scale);
unreachable();
} }
} }
@ -528,8 +483,7 @@ static inline std::string vtx_attr(const ShaderConfig& config, GXAttr attr) {
// Default normal // Default normal
return "vec3<f32>(1.0, 0.0, 0.0)"s; return "vec3<f32>(1.0, 0.0, 0.0)"s;
} }
Log.report(LOG_FATAL, FMT_STRING("unmapped attr {}"), attr); UNLIKELY FATAL("unmapped vtx attr {}", static_cast<int>(attr));
unreachable();
} }
if (attr == GX_VA_POS) { if (attr == GX_VA_POS) {
return "in_pos"s; return "in_pos"s;
@ -545,8 +499,7 @@ static inline std::string vtx_attr(const ShaderConfig& config, GXAttr attr) {
const auto idx = attr - GX_VA_TEX0; const auto idx = attr - GX_VA_TEX0;
return fmt::format(FMT_STRING("in_tex{}_uv"), idx); return fmt::format(FMT_STRING("in_tex{}_uv"), idx);
} }
Log.report(LOG_FATAL, FMT_STRING("unhandled attr {}"), attr); UNLIKELY FATAL("unhandled vtx attr {}", static_cast<int>(attr));
unreachable();
} }
static inline std::string texture_conversion(const TextureConfig& tex, u32 stageIdx, u32 texMapId) { static inline std::string texture_conversion(const TextureConfig& tex, u32 stageIdx, u32 texMapId) {
@ -705,12 +658,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
const auto hash = xxh3_hash(config); const auto hash = xxh3_hash(config);
const auto it = g_gxCachedShaders.find(hash); const auto it = g_gxCachedShaders.find(hash);
if (it != g_gxCachedShaders.end()) { if (it != g_gxCachedShaders.end()) {
#ifndef NDEBUG CHECK(g_gxCachedShaderConfigs[hash] == config, "Shader collision! {:x}", hash);
if (g_gxCachedShaderConfigs[hash] != config) {
Log.report(LOG_FATAL, FMT_STRING("Shader collision!"));
unreachable();
}
#endif
return it->second.first; return it->second.first;
} }
@ -868,6 +816,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
{ {
std::string outReg; std::string outReg;
switch (stage.colorOp.outReg) { switch (stage.colorOp.outReg) {
DEFAULT_FATAL("invalid colorOp outReg {}", static_cast<int>(stage.colorOp.outReg));
case GX_TEVPREV: case GX_TEVPREV:
outReg = "prev"; outReg = "prev";
break; break;
@ -880,8 +829,6 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
case GX_TEVREG2: case GX_TEVREG2:
outReg = "tevreg2"; outReg = "tevreg2";
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid colorOp outReg {}"), stage.colorOp.outReg);
} }
std::string op = fmt::format( std::string op = fmt::format(
FMT_STRING("(({4}mix({0}, {1}, {2}) + {3}){5}){6}"), color_arg_reg(stage.colorPass.a, idx, config, stage), FMT_STRING("(({4}mix({0}, {1}, {2}) + {3}){5}){6}"), color_arg_reg(stage.colorPass.a, idx, config, stage),
@ -897,6 +844,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
{ {
std::string outReg; std::string outReg;
switch (stage.alphaOp.outReg) { switch (stage.alphaOp.outReg) {
DEFAULT_FATAL("invalid alphaOp outReg {}", static_cast<int>(stage.alphaOp.outReg));
case GX_TEVPREV: case GX_TEVPREV:
outReg = "prev.a"; outReg = "prev.a";
break; break;
@ -909,8 +857,6 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
case GX_TEVREG2: case GX_TEVREG2:
outReg = "tevreg2.a"; outReg = "tevreg2.a";
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid alphaOp outReg {}"), stage.alphaOp.outReg);
} }
std::string op = fmt::format( std::string op = fmt::format(
FMT_STRING("(({4}mix({0}, {1}, {2}) + {3}){5}){6}"), alpha_arg_reg(stage.alphaPass.a, idx, config, stage), FMT_STRING("(({4}mix({0}, {1}, {2}) + {3}){5}){6}"), alpha_arg_reg(stage.alphaPass.a, idx, config, stage),
@ -1029,7 +975,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
attn = max(0.0, cos_attn / dist_attn);)""")); attn = max(0.0, cos_attn / dist_attn);)"""));
} else if (cc.attnFn == GX_AF_SPEC) { } else if (cc.attnFn == GX_AF_SPEC) {
diffFn = GX_DF_NONE; diffFn = GX_DF_NONE;
Log.report(LOG_FATAL, FMT_STRING("AF_SPEC unimplemented")); FATAL("AF_SPEC unimplemented");
} }
if (diffFn == GX_DF_NONE) { if (diffFn == GX_DF_NONE) {
lightDiffFn = "1.0"; lightDiffFn = "1.0";
@ -1116,10 +1062,8 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{} = vec4<f32>(in_pos, 1.0);"), i); vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{} = vec4<f32>(in_pos, 1.0);"), i);
} else if (tcg.src == GX_TG_NRM) { } else if (tcg.src == GX_TG_NRM) {
vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{} = vec4<f32>(in_nrm, 1.0);"), i); vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{} = vec4<f32>(in_nrm, 1.0);"), i);
} else { } else
Log.report(LOG_FATAL, FMT_STRING("unhandled tcg src {} for "), tcg.src); UNLIKELY FATAL("unhandled tcg src {}", static_cast<int>(tcg.src));
unreachable();
}
if (tcg.mtx == GX_IDENTITY) { if (tcg.mtx == GX_IDENTITY) {
vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{0}_tmp = tc{0}.xyz;"), i); vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{0}_tmp = tc{0}.xyz;"), i);
} else { } else {
@ -1152,6 +1096,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
std::string_view suffix; std::string_view suffix;
if (!is_palette_format(texConfig.copyFmt)) { if (!is_palette_format(texConfig.copyFmt)) {
switch (texConfig.loadFmt) { switch (texConfig.loadFmt) {
DEFAULT_FATAL("unimplemented palette format {}", static_cast<int>(texConfig.loadFmt));
case GX_TF_C4: case GX_TF_C4:
suffix = "I4"sv; suffix = "I4"sv;
break; break;
@ -1161,9 +1106,6 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
// case GX_TF_C14X2: // case GX_TF_C14X2:
// suffix = "I14X2"; // suffix = "I14X2";
// break; // break;
default:
Log.report(LOG_FATAL, FMT_STRING("Unsupported palette format {}"), texConfig.loadFmt);
unreachable();
} }
} }
fragmentFnPre += fragmentFnPre +=
@ -1179,15 +1121,13 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
for (int i = 0; i < info.usesTexMtx.size(); ++i) { for (int i = 0; i < info.usesTexMtx.size(); ++i) {
if (info.usesTexMtx.test(i)) { if (info.usesTexMtx.test(i)) {
switch (info.texMtxTypes[i]) { switch (info.texMtxTypes[i]) {
DEFAULT_FATAL("unhandled tex mtx type {}", static_cast<int>(info.texMtxTypes[i]));
case GX_TG_MTX2x4: case GX_TG_MTX2x4:
uniBufAttrs += fmt::format(FMT_STRING("\n texmtx{}: mat4x2<f32>,"), i); uniBufAttrs += fmt::format(FMT_STRING("\n texmtx{}: mat4x2<f32>,"), i);
break; break;
case GX_TG_MTX3x4: case GX_TG_MTX3x4:
uniBufAttrs += fmt::format(FMT_STRING("\n texmtx{}: mat4x3<f32>,"), i); uniBufAttrs += fmt::format(FMT_STRING("\n texmtx{}: mat4x3<f32>,"), i);
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("unhandled tex mtx type {}"), info.texMtxTypes[i]);
unreachable();
} }
} }
} }
@ -1210,6 +1150,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
fragmentFn += "\n // Fog\n var fogF = clamp((ubuf.fog.a / (ubuf.fog.b - in.pos.z)) - ubuf.fog.c, 0.0, 1.0);"; fragmentFn += "\n // Fog\n var fogF = clamp((ubuf.fog.a / (ubuf.fog.b - in.pos.z)) - ubuf.fog.c, 0.0, 1.0);";
switch (config.fogType) { switch (config.fogType) {
DEFAULT_FATAL("invalid fog type {}", static_cast<int>(config.fogType));
case GX_FOG_PERSP_LIN: case GX_FOG_PERSP_LIN:
case GX_FOG_ORTHO_LIN: case GX_FOG_ORTHO_LIN:
fragmentFn += "\n var fogZ = fogF;"; fragmentFn += "\n var fogZ = fogF;";
@ -1232,9 +1173,6 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
"\n fogF = 1.0 - fogF;" "\n fogF = 1.0 - fogF;"
"\n var fogZ = exp2(-8.0 * fogF * fogF);"; "\n var fogZ = exp2(-8.0 * fogF * fogF);";
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid fog type {}"), config.fogType);
unreachable();
} }
fragmentFn += "\n prev = vec4<f32>(mix(prev.rgb, ubuf.fog.color.rgb, clamp(fogZ, 0.0, 1.0)), prev.a);"; fragmentFn += "\n prev = vec4<f32>(mix(prev.rgb, ubuf.fog.color.rgb, clamp(fogZ, 0.0, 1.0)), prev.a);";
} }
@ -1274,6 +1212,7 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
if (comp0Valid || comp1Valid) { if (comp0Valid || comp1Valid) {
fragmentFn += "\n // Alpha compare"; fragmentFn += "\n // Alpha compare";
switch (config.alphaCompare.op) { switch (config.alphaCompare.op) {
DEFAULT_FATAL("invalid alpha compare op {}", static_cast<int>(config.alphaCompare.op));
case GX_AOP_AND: case GX_AOP_AND:
fragmentFn += fmt::format(FMT_STRING("\n if (!({} && {})) {{ discard; }}"), comp0, comp1); fragmentFn += fmt::format(FMT_STRING("\n if (!({} && {})) {{ discard; }}"), comp0, comp1);
break; break;
@ -1286,9 +1225,6 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
case GX_AOP_XNOR: case GX_AOP_XNOR:
fragmentFn += fmt::format(FMT_STRING("\n if (({} ^^ {})) {{ discard; }}"), comp0, comp1); fragmentFn += fmt::format(FMT_STRING("\n if (({} ^^ {})) {{ discard; }}"), comp0, comp1);
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("invalid alpha compare op {}"), config.alphaCompare.op);
unreachable();
} }
} }
} }

View File

@ -66,11 +66,14 @@ static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u
for (int attr = 0; attr < GX_VA_MAX_ATTR; attr++) { for (int attr = 0; attr < GX_VA_MAX_ATTR; attr++) {
const auto& attrFmt = g_gxState.vtxFmts[vtxfmt].attrs[attr]; const auto& attrFmt = g_gxState.vtxFmts[vtxfmt].attrs[attr];
switch (g_gxState.vtxDesc[attr]) { switch (g_gxState.vtxDesc[attr]) {
DEFAULT_FATAL("unhandled attribute type {}", static_cast<int>(g_gxState.vtxDesc[attr]));
case GX_NONE: case GX_NONE:
break; break;
case GX_DIRECT: case GX_DIRECT:
#define COMBINE(val1, val2, val3) (((val1) << 16) | ((val2) << 8) | (val3)) #define COMBINE(val1, val2, val3) (((val1) << 16) | ((val2) << 8) | (val3))
switch (COMBINE(attr, attrFmt.cnt, attrFmt.type)) { switch (COMBINE(attr, attrFmt.cnt, attrFmt.type)) {
DEFAULT_FATAL("not handled: attr {}, cnt {}, type {}", static_cast<int>(attr), static_cast<int>(attrFmt.cnt),
static_cast<int>(attrFmt.type));
case COMBINE(GX_VA_POS, GX_POS_XYZ, GX_F32): case COMBINE(GX_VA_POS, GX_POS_XYZ, GX_F32):
case COMBINE(GX_VA_NRM, GX_NRM_XYZ, GX_F32): case COMBINE(GX_VA_NRM, GX_NRM_XYZ, GX_F32):
attrArrays[attr].count = 3; attrArrays[attr].count = 3;
@ -118,9 +121,6 @@ static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u
vtxSize += 4; vtxSize += 4;
outVtxSize += 16; outVtxSize += 16;
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("not handled: attr {}, cnt {}, type {}"), attr, attrFmt.cnt, attrFmt.type);
break;
} }
#undef COMBINE #undef COMBINE
break; break;
@ -134,8 +134,6 @@ static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u
outVtxSize += 2; outVtxSize += 2;
indexedAttrs[attr] = true; indexedAttrs[attr] = true;
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("unhandled attribute type {}"), g_gxState.vtxDesc[attr]);
} }
} }
// Align to 4 // Align to 4
@ -263,9 +261,8 @@ static u16 prepare_idx_buffer(ByteBuffer& buf, GXPrimitive prim, u16 vtxStart, u
} }
numIndices += 3; numIndices += 3;
} }
} else { } else
Log.report(LOG_FATAL, FMT_STRING("Unsupported primitive type {}"), static_cast<u32>(prim)); UNLIKELY FATAL("unsupported primitive type {}", static_cast<u32>(prim));
}
return numIndices; return numIndices;
} }
@ -293,6 +290,7 @@ void queue_surface(const u8* dlStart, u32 dlSize) noexcept {
u8 opcode = cmd & GX_OPCODE_MASK; u8 opcode = cmd & GX_OPCODE_MASK;
switch (opcode) { switch (opcode) {
DEFAULT_FATAL("unimplemented opcode: {}", opcode);
case GX_NOP: case GX_NOP:
continue; continue;
case GX_LOAD_BP_REG: case GX_LOAD_BP_REG:
@ -315,10 +313,7 @@ void queue_surface(const u8* dlStart, u32 dlSize) noexcept {
case GX_DRAW_LINES: case GX_DRAW_LINES:
case GX_DRAW_LINE_STRIP: case GX_DRAW_LINE_STRIP:
case GX_DRAW_POINTS: case GX_DRAW_POINTS:
Log.report(LOG_FATAL, FMT_STRING("unimplemented prim type: {}"), opcode); FATAL("unimplemented prim type: {}", opcode);
break;
default:
Log.report(LOG_FATAL, FMT_STRING("unimplemented opcode: {}"), opcode);
break; break;
} }
} }
@ -415,6 +410,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const
} }
const auto attr = static_cast<GXAttr>(i); const auto attr = static_cast<GXAttr>(i);
switch (attr) { switch (attr) {
DEFAULT_FATAL("unhandled direct attr {}", i);
case GX_VA_POS: case GX_VA_POS:
case GX_VA_NRM: case GX_VA_NRM:
vtxAttrs[shaderLocation] = wgpu::VertexAttribute{ vtxAttrs[shaderLocation] = wgpu::VertexAttribute{
@ -448,8 +444,6 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const
}; };
offset += 8; offset += 8;
break; break;
default:
Log.report(LOG_FATAL, FMT_STRING("unhandled direct attr {}"), i);
} }
++shaderLocation; ++shaderLocation;
} }

View File

@ -21,6 +21,7 @@ struct TextureFormatInfo {
}; };
static TextureFormatInfo format_info(wgpu::TextureFormat format) { static TextureFormatInfo format_info(wgpu::TextureFormat format) {
switch (format) { switch (format) {
DEFAULT_FATAL("unimplemented texture format {}", magic_enum::enum_name(format));
case wgpu::TextureFormat::R8Unorm: case wgpu::TextureFormat::R8Unorm:
return {1, 1, 1, false}; return {1, 1, 1, false};
case wgpu::TextureFormat::R16Sint: case wgpu::TextureFormat::R16Sint:
@ -30,9 +31,6 @@ static TextureFormatInfo format_info(wgpu::TextureFormat format) {
return {1, 1, 4, false}; return {1, 1, 4, false};
case wgpu::TextureFormat::BC1RGBAUnorm: case wgpu::TextureFormat::BC1RGBAUnorm:
return {4, 4, 8, true}; return {4, 4, 8, true};
default:
Log.report(LOG_FATAL, FMT_STRING("format_info: unimplemented format {}"), magic_enum::enum_name(format));
unreachable();
} }
} }
static wgpu::Extent3D physical_size(wgpu::Extent3D size, TextureFormatInfo info) { static wgpu::Extent3D physical_size(wgpu::Extent3D size, TextureFormatInfo info) {
@ -67,11 +65,8 @@ TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mi
const uint32_t heightBlocks = physicalSize.height / info.blockHeight; const uint32_t heightBlocks = physicalSize.height / info.blockHeight;
const uint32_t bytesPerRow = widthBlocks * info.blockSize; const uint32_t bytesPerRow = widthBlocks * info.blockSize;
const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers; const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers;
if (offset + dataSize > data.size()) { CHECK(offset + dataSize <= data.size(), "new_static_texture_2d[{}]: expected at least {} bytes, got {}", label,
Log.report(LOG_FATAL, FMT_STRING("new_static_texture_2d[{}]: expected at least {} bytes, got {}"), label,
offset + dataSize, data.size()); offset + dataSize, data.size());
unreachable();
}
const wgpu::ImageCopyTexture dstView{ const wgpu::ImageCopyTexture dstView{
.texture = ref.texture, .texture = ref.texture,
.mipLevel = mip, .mipLevel = mip,
@ -176,11 +171,8 @@ void write_texture(const TextureRef& ref, ArrayRef<uint8_t> data) noexcept {
const uint32_t heightBlocks = physicalSize.height / info.blockHeight; const uint32_t heightBlocks = physicalSize.height / info.blockHeight;
const uint32_t bytesPerRow = widthBlocks * info.blockSize; const uint32_t bytesPerRow = widthBlocks * info.blockSize;
const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers; const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers;
if (offset + dataSize > data.size()) { CHECK(offset + dataSize <= data.size(), "write_texture: expected at least {} bytes, got {}", offset + dataSize,
Log.report(LOG_FATAL, FMT_STRING("write_texture: expected at least {} bytes, got {}"), offset + dataSize,
data.size()); data.size());
unreachable();
}
// auto dstView = wgpu::ImageCopyTexture{ // auto dstView = wgpu::ImageCopyTexture{
// .texture = ref.texture, // .texture = ref.texture,
// .mipLevel = mip, // .mipLevel = mip,

View File

@ -35,8 +35,6 @@ struct TextureRef {
, isRenderTexture(isRenderTexture) {} , isRenderTexture(isRenderTexture) {}
}; };
using TextureHandle = std::shared_ptr<TextureRef>;
TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format, ArrayRef<uint8_t> data, TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format, ArrayRef<uint8_t> data,
const char* label) noexcept; const char* label) noexcept;
TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format, TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format,

View File

@ -569,9 +569,7 @@ ByteBuffer BuildRGBA8FromCMPR(uint32_t width, uint32_t height, uint32_t mips, Ar
ByteBuffer convert_texture(u32 format, uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) { ByteBuffer convert_texture(u32 format, uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
switch (format) { switch (format) {
default: DEFAULT_FATAL("convert_texture: unknown texture format {}", format);
Log.report(LOG_FATAL, FMT_STRING("convert_texture: unknown format supplied {}"), format);
unreachable();
case GX_TF_R8_PC: case GX_TF_R8_PC:
case GX_TF_RGBA8_PC: case GX_TF_RGBA8_PC:
return {}; // No conversion return {}; // No conversion
@ -588,8 +586,7 @@ ByteBuffer convert_texture(u32 format, uint32_t width, uint32_t height, uint32_t
case GX_TF_C8: case GX_TF_C8:
return BuildC8FromGCN(width, height, mips, data); return BuildC8FromGCN(width, height, mips, data);
case GX_TF_C14X2: case GX_TF_C14X2:
Log.report(LOG_FATAL, FMT_STRING("convert_texture: C14X2 unimplemented")); FATAL("convert_texture: C14X2 unimplemented");
unreachable();
case GX_TF_RGB565: case GX_TF_RGB565:
return BuildRGB565FromGCN(width, height, mips, data); return BuildRGB565FromGCN(width, height, mips, data);
case GX_TF_RGB5A3: case GX_TF_RGB5A3:

View File

@ -32,6 +32,29 @@ using namespace std::string_view_literals;
#define ALIGN(x, a) (((x) + ((a)-1)) & ~((a)-1)) #define ALIGN(x, a) (((x) + ((a)-1)) & ~((a)-1))
#endif #endif
#if !defined(__has_cpp_attribute)
#define __has_cpp_attribute(name) 0
#endif
#if __has_cpp_attribute(unlikely)
#define UNLIKELY [[unlikely]]
#else
#define UNLIKELY
#endif
#define FATAL(msg, ...) \
{ \
Log.report(LOG_FATAL, FMT_STRING(msg), ##__VA_ARGS__); \
unreachable(); \
}
#define ASSERT(cond, msg, ...) \
if (!(cond)) \
UNLIKELY FATAL(msg, ##__VA_ARGS__)
#ifdef NDEBUG
#define CHECK
#else
#define CHECK(cond, msg, ...) ASSERT(cond, msg, ##__VA_ARGS__)
#endif
#define DEFAULT_FATAL(msg, ...) UNLIKELY default: FATAL(msg, ##__VA_ARGS__)
namespace aurora { namespace aurora {
extern AuroraConfig g_config; extern AuroraConfig g_config;
@ -72,8 +95,6 @@ public:
template <size_t N> template <size_t N>
constexpr ArrayRef(const std::array<T, N>& arr) : ptr(arr.data()), length(arr.size()) {} constexpr ArrayRef(const std::array<T, N>& arr) : ptr(arr.data()), length(arr.size()) {}
ArrayRef(const std::vector<T>& vec) : ptr(vec.data()), length(vec.size()) {} ArrayRef(const std::vector<T>& vec) : ptr(vec.data()), length(vec.size()) {}
// template <size_t N>
// ArrayRef(const rstl::reserved_vector<T, N>& vec) : ptr(vec.data()), length(vec.size()) {}
const T* data() const { return ptr; } const T* data() const { return ptr; }
size_t size() const { return length; } size_t size() const { return length; }

View File

@ -268,8 +268,7 @@ void create_copy_bind_group() {
} }
static void error_callback(WGPUErrorType type, char const* message, void* userdata) { static void error_callback(WGPUErrorType type, char const* message, void* userdata) {
Log.report(LOG_FATAL, FMT_STRING("WebGPU error {}: {}"), magic_enum::enum_name(static_cast<WGPUErrorType>(type)), FATAL("WebGPU error {}: {}", static_cast<int>(type), message);
message);
} }
#ifndef WEBGPU_DAWN #ifndef WEBGPU_DAWN
@ -378,9 +377,7 @@ bool initialize(AuroraBackend auroraBackend) {
.label = "Surface", .label = "Surface",
}; };
g_surface = wgpu::Surface::Acquire(wgpuInstanceCreateSurface(g_instance.Get(), &surfaceDescriptor)); g_surface = wgpu::Surface::Acquire(wgpuInstanceCreateSurface(g_instance.Get(), &surfaceDescriptor));
if (!g_surface) { ASSERT(g_surface, "Failed to initialize surface");
Log.report(LOG_FATAL, FMT_STRING("Failed to initialize surface"));
}
const WGPURequestAdapterOptions options{ const WGPURequestAdapterOptions options{
.compatibleSurface = g_surface.Get(), .compatibleSurface = g_surface.Get(),
.powerPreference = WGPUPowerPreference_HighPerformance, .powerPreference = WGPUPowerPreference_HighPerformance,

View File

@ -2,3 +2,8 @@
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
#include <emscripten.h> #include <emscripten.h>
#endif #endif
static inline bool operator==(const wgpu::Extent3D& lhs, const wgpu::Extent3D& rhs) {
return lhs.width == rhs.width && lhs.height == rhs.height && lhs.depthOrArrayLayers == rhs.depthOrArrayLayers;
}
static inline bool operator!=(const wgpu::Extent3D& lhs, const wgpu::Extent3D& rhs) { return !(lhs == rhs); }

View File

@ -109,10 +109,7 @@ static void set_window_icon() noexcept {
} }
auto* iconSurface = SDL_CreateRGBSurfaceFrom(g_config.iconRGBA8, g_config.iconWidth, g_config.iconHeight, 32, auto* iconSurface = SDL_CreateRGBSurfaceFrom(g_config.iconRGBA8, g_config.iconWidth, g_config.iconHeight, 32,
4 * g_config.iconWidth, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); 4 * g_config.iconWidth, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
if (iconSurface == nullptr) { ASSERT(iconSurface != nullptr, "Failed to create icon surface: {}", SDL_GetError());
Log.report(LOG_FATAL, FMT_STRING("Failed to create icon surface: {}"), SDL_GetError());
unreachable();
}
SDL_SetWindowIcon(g_window, iconSurface); SDL_SetWindowIcon(g_window, iconSurface);
SDL_FreeSurface(iconSurface); SDL_FreeSurface(iconSurface);
} }
@ -197,10 +194,7 @@ void show_window() {
} }
bool initialize() { bool initialize() {
if (SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC) < 0) { ASSERT(SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC) == 0, "Error initializing SDL: {}", SDL_GetError());
Log.report(LOG_FATAL, FMT_STRING("Error initializing SDL: {}"), SDL_GetError());
unreachable();
}
#if !defined(_WIN32) && !defined(__APPLE__) #if !defined(_WIN32) && !defined(__APPLE__)
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");