mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-12-08 10:25:59 +03:00
1320 lines
38 KiB
C++
1320 lines
38 KiB
C++
#include "gx/CGxDevice.hpp"
|
|
#include "gx/Gx.hpp"
|
|
#include "gx/Shader.hpp"
|
|
#include "gx/texture/CGxTex.hpp"
|
|
#include "gx/Texture.hpp"
|
|
#include "gx/RenderState.hpp"
|
|
#include "gx/Transform.hpp"
|
|
#include "gx/Draw.hpp"
|
|
#include "util/SFile.hpp"
|
|
#include "event/Input.hpp"
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <new>
|
|
#include <storm/Error.hpp>
|
|
#include <storm/Memory.hpp>
|
|
|
|
#if defined(WHOA_SYSTEM_WIN)
|
|
#include "gx/d3d/CGxDeviceD3d.hpp"
|
|
#endif
|
|
|
|
#if defined(WHOA_BUILD_GLSDL)
|
|
#include "gx/glsdl/CGxDeviceGLSDL.hpp"
|
|
#endif
|
|
|
|
#if defined(WHOA_SYSTEM_MAC)
|
|
#include "gx/gll/CGxDeviceGLL.hpp"
|
|
#endif
|
|
|
|
uint32_t CGxDevice::s_alphaRef[] = {
|
|
0, // GxBlend_Opaque
|
|
224, // GxBlend_AlphaKey
|
|
1, // GxBlend_Alpha
|
|
1, // GxBlend_Add
|
|
1, // GxBlend_Mod
|
|
1, // GxBlend_Mod2x
|
|
1, // GxBlend_ModAdd
|
|
0, // GxBlend_InvSrcAlphaAdd
|
|
0, // GxBlend_InvSrcAlphaOpaque
|
|
0, // GxBlend_SrcAlphaOpaque
|
|
0, // GxBlend_NoAlphaAdd
|
|
0 // GxBlend_ConstantAlpha
|
|
};
|
|
|
|
C3Vector CGxDevice::s_pointScaleIdentity = { 1.0f, 0.0f, 0.0f };
|
|
|
|
uint32_t CGxDevice::s_primVtxAdjust[] = {
|
|
0, // GxPrim_Points
|
|
0, // GxPrim_Lines
|
|
1, // GxPrim_LineStrip
|
|
0, // GxPrim_Triangles
|
|
2, // GxPrim_TriangleStrip
|
|
2, // GxPrim_TriangleFan
|
|
};
|
|
|
|
uint32_t CGxDevice::s_primVtxDiv[] = {
|
|
1, // GxPrim_Points
|
|
2, // GxPrim_Lines
|
|
1, // GxPrim_LineStrip
|
|
3, // GxPrim_Triangles
|
|
1, // GxPrim_TriangleStrip
|
|
1, // GxPrim_TriangleFan
|
|
};
|
|
|
|
ShaderConstants CGxDevice::s_shadowConstants[2];
|
|
|
|
uint32_t CGxDevice::s_streamPoolSize[] = {
|
|
0x2C0000, // GxPoolTarget_Vertex
|
|
0x40000 // GxPoolTarget_Index
|
|
};
|
|
|
|
uint32_t CGxDevice::s_texFormatBitDepth[] = {
|
|
0, // GxTex_Unknown
|
|
32, // GxTex_Abgr8888
|
|
32, // GxTex_Argb8888
|
|
16, // GxTex_Argb4444
|
|
16, // GxTex_Argb1555
|
|
16, // GxTex_Rgb565
|
|
4, // GxTex_Dxt1
|
|
8, // GxTex_Dxt3
|
|
8, // GxTex_Dxt5
|
|
16, // GxTex_Uv88
|
|
32, // GxTex_Gr1616F
|
|
32, // GxTex_R32F
|
|
32 // GxTex_D24X8
|
|
};
|
|
|
|
uint32_t CGxDevice::s_texFormatBytesPerBlock[] = {
|
|
0, // GxTex_Unknown
|
|
4, // GxTex_Abgr8888
|
|
4, // GxTex_Argb8888
|
|
2, // GxTex_Argb4444
|
|
2, // GxTex_Argb1555
|
|
2, // GxTex_Rgb565
|
|
8, // GxTex_Dxt1
|
|
16, // GxTex_Dxt3
|
|
16, // GxTex_Dxt5
|
|
2, // GxTex_Uv88
|
|
4, // GxTex_Gr1616F
|
|
4, // GxTex_R32F
|
|
4 // GxTex_D24X8
|
|
};
|
|
|
|
CGxShader* CGxDevice::s_uiVertexShader = nullptr;
|
|
CGxShader* CGxDevice::s_uiPixelShader = nullptr;
|
|
|
|
void CGxDevice::Log(const char* format, ...) {
|
|
// TODO
|
|
}
|
|
|
|
void CGxDevice::Log(const CGxFormat& format) {
|
|
// TODO
|
|
}
|
|
|
|
#if defined(WHOA_SYSTEM_WIN)
|
|
CGxDevice* CGxDevice::NewD3d() {
|
|
auto m = SMemAlloc(sizeof(CGxDeviceD3d), __FILE__, __LINE__, 0x0);
|
|
return new (m) CGxDeviceD3d();
|
|
}
|
|
|
|
CGxDevice* CGxDevice::NewD3d9Ex() {
|
|
// TODO
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WHOA_SYSTEM_MAC)
|
|
CGxDevice* CGxDevice::NewGLL() {
|
|
auto m = SMemAlloc(sizeof(CGxDeviceGLL), __FILE__, __LINE__, 0x0);
|
|
return new (m) CGxDeviceGLL();
|
|
}
|
|
#endif
|
|
|
|
CGxDevice* CGxDevice::NewOpenGl() {
|
|
return nullptr;
|
|
}
|
|
|
|
#if defined(WHOA_BUILD_GLSDL)
|
|
CGxDevice* CGxDevice::NewGLSDL() {
|
|
auto m = SMemAlloc(sizeof(CGxDeviceGLSDL), __FILE__, __LINE__, 0x0);
|
|
return new (m) CGxDeviceGLSDL();
|
|
}
|
|
#endif
|
|
|
|
uint32_t CGxDevice::PrimCalcCount(EGxPrim primType, uint32_t count) {
|
|
auto div = CGxDevice::s_primVtxDiv[primType];
|
|
if (div != 1) {
|
|
count /= div;
|
|
}
|
|
|
|
return count - CGxDevice::s_primVtxAdjust[primType];
|
|
}
|
|
|
|
void CGxDevice::ICursorUpdate(EGxTexCommand command, uint32_t width, uint32_t height, uint32_t face, uint32_t level, void* userArg, uint32_t& texelStrideInBytes, const void*& texels) {
|
|
// TODO
|
|
if (command == GxTex_Latch) {
|
|
auto device = static_cast<CGxDevice*>(userArg);
|
|
|
|
texelStrideInBytes = 0x80;
|
|
texels = device->m_cursor;
|
|
}
|
|
}
|
|
|
|
CGxDevice::CGxDevice() {
|
|
// TODO
|
|
// - implement rest of constructor
|
|
|
|
this->IRsInit();
|
|
|
|
// Set default viewport
|
|
this->m_viewport.x = { 0.0f, 1.0f };
|
|
this->m_viewport.y = { 0.0f, 1.0f };
|
|
this->m_viewport.z = { 0.0f, 1.0f };
|
|
|
|
// Turn on all master enables
|
|
this->m_appMasterEnables = 511;
|
|
this->m_hwMasterEnables = 511;
|
|
|
|
this->ShaderConstantsClear();
|
|
}
|
|
|
|
CGxBuf* CGxDevice::BufCreate(CGxPool* pool, uint32_t itemSize, uint32_t itemCount, uint32_t index) {
|
|
auto m = SMemAlloc(sizeof(CGxBuf), __FILE__, __LINE__, 0x0);
|
|
auto buf = new (m) CGxBuf(pool, itemSize, itemCount, index);
|
|
|
|
pool->m_bufList.LinkToTail(buf);
|
|
|
|
return buf;
|
|
}
|
|
|
|
char* CGxDevice::BufLock(CGxBuf* buf) {
|
|
buf->unk1E = 1;
|
|
buf->unk1F = 0;
|
|
|
|
this->m_bufLocked[buf->m_pool->m_target] = buf;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
CGxBuf* CGxDevice::BufStream(EGxPoolTarget target, uint32_t itemSize, uint32_t itemCount) {
|
|
CGxBuf* buf = this->m_streamBufs[target];
|
|
CGxPool* pool = buf->m_pool;
|
|
|
|
if (pool && pool->m_size < itemSize * itemCount) {
|
|
this->PoolSizeSet(pool, itemSize * itemCount);
|
|
}
|
|
|
|
buf->m_itemSize = itemSize;
|
|
buf->m_itemCount = itemCount;
|
|
buf->m_size = itemSize * itemCount;
|
|
buf->unk1C = 0;
|
|
|
|
return buf;
|
|
}
|
|
|
|
int32_t CGxDevice::BufUnlock(CGxBuf* buf, uint32_t size) {
|
|
this->m_bufLocked[buf->m_pool->m_target] = nullptr;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CGxDevice::BufData(CGxBuf* buf, const void* data, size_t size, uintptr_t offset) {
|
|
buf->unk1E = 1;
|
|
buf->unk1F = 0;
|
|
}
|
|
|
|
const CGxCaps& CGxDevice::Caps() const {
|
|
return this->m_caps;
|
|
}
|
|
|
|
int32_t CGxDevice::DeviceCreate(int32_t (*windowProc)(void* window, uint32_t message, uintptr_t wparam, intptr_t lparam), const CGxFormat& format) {
|
|
this->m_windowProc = windowProc;
|
|
|
|
return this->DeviceSetFormat(format);
|
|
}
|
|
|
|
void CGxDevice::DeviceCreatePools() {
|
|
this->m_vertexPool = this->PoolCreate(
|
|
GxPoolTarget_Vertex,
|
|
GxPoolUsage_Stream,
|
|
CGxDevice::s_streamPoolSize[GxPoolTarget_Vertex],
|
|
GxPoolHintBit_Unk3,
|
|
"Stream_vtx"
|
|
);
|
|
|
|
this->m_indexPool = this->PoolCreate(
|
|
GxPoolTarget_Index,
|
|
GxPoolUsage_Stream,
|
|
CGxDevice::s_streamPoolSize[GxPoolTarget_Index],
|
|
GxPoolHintBit_Unk3,
|
|
"Stream_idx"
|
|
);
|
|
}
|
|
|
|
void CGxDevice::DeviceCreateStreamBufs() {
|
|
this->m_streamBufs[GxPoolTarget_Vertex] = this->BufCreate(this->m_vertexPool, 0, 0, 0);
|
|
this->m_streamBufs[GxPoolTarget_Index] = this->BufCreate(this->m_indexPool, 0, 0, 0);
|
|
}
|
|
|
|
const CRect& CGxDevice::DeviceCurWindow() {
|
|
return this->m_curWindowRect;
|
|
}
|
|
|
|
int32_t CGxDevice::DeviceSetFormat(const CGxFormat& format) {
|
|
memcpy(&this->m_format, &format, sizeof(this->m_format));
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CGxDevice::DeviceSetCurWindow(const CRect& rect) {
|
|
this->m_curWindowRect = rect;
|
|
}
|
|
|
|
void CGxDevice::DeviceSetDefWindow(const CRect& rect) {
|
|
this->m_defWindowRect = rect;
|
|
this->DeviceSetCurWindow(rect);
|
|
}
|
|
|
|
const CRect& CGxDevice::DeviceDefWindow() {
|
|
return this->m_defWindowRect;
|
|
}
|
|
|
|
void CGxDevice::ICursorCreate(const CGxFormat& format) {
|
|
int32_t hardwareCursor = format.hwCursor && this->m_caps.m_hardwareCursor;
|
|
|
|
this->m_hardwareCursor = hardwareCursor;
|
|
|
|
// If hardware cursor is disabled, and there is no cursor texture yet, create one
|
|
if (!hardwareCursor && this->m_cursorTexture == nullptr) {
|
|
// default flags?
|
|
CGxTexFlags cursorTextureFlags;
|
|
|
|
// Create a 32x32 cursor texture
|
|
GxTexCreate(
|
|
32,
|
|
32,
|
|
GxTex_Argb8888,
|
|
cursorTextureFlags,
|
|
reinterpret_cast<void*>(this),
|
|
CGxDevice::ICursorUpdate,
|
|
this->m_cursorTexture
|
|
);
|
|
}
|
|
}
|
|
|
|
void CGxDevice::ICursorDestroy() {
|
|
if (this->m_cursorTexture) {
|
|
GxTexDestroy(this->m_cursorTexture);
|
|
this->m_cursorTexture = nullptr;
|
|
}
|
|
}
|
|
|
|
void CGxDevice::ICursorDraw() {
|
|
if (!this->m_cursorVisible) {
|
|
return;
|
|
}
|
|
|
|
if (this->m_hardwareCursor) {
|
|
return;
|
|
}
|
|
|
|
int32_t mouseX;
|
|
int32_t mouseY;
|
|
OsInputGetMousePosition(&mouseX, &mouseY);
|
|
|
|
if (mouseX <= -1 || mouseY <= -1 || mouseX >= this->m_curWindowRect.maxX || mouseY >= this->m_curWindowRect.maxY) {
|
|
return;
|
|
}
|
|
|
|
GxRsPush();
|
|
// Turn off everything
|
|
GxRsSet(GxRs_PolygonOffset, 0);
|
|
GxRsSet(GxRs_NormalizeNormals, 0);
|
|
GxRsSet(GxRs_BlendingMode, GxBlend_AlphaKey);
|
|
GxRsSetAlphaRef();
|
|
GxRsSet(GxRs_Lighting, 0);
|
|
GxRsSet(GxRs_Fog, 0);
|
|
GxRsSet(GxRs_DepthTest, 0);
|
|
GxRsSet(GxRs_DepthWrite, 0);
|
|
GxRsSet(GxRs_ColorWrite, 15);
|
|
GxRsSet(GxRs_Culling, 0);
|
|
GxRsSet(GxRs_ClipPlaneMask, 0);
|
|
GxRsSet(GxRs_Texture0, this->m_cursorTexture);
|
|
GxRsSet(GxRs_Texture1, static_cast<CGxTex*>(nullptr));
|
|
GxRsSet(GxRs_ColorOp0, 0);
|
|
GxRsSet(GxRs_AlphaOp0, 0);
|
|
GxRsSet(GxRs_TexGen0, 0);
|
|
GxRsSet(GxRs_Unk61, 0);
|
|
|
|
C44Matrix identity;
|
|
GxXformPush(GxXform_World, identity);
|
|
|
|
float cursorDepth = 1.0f;
|
|
|
|
C44Matrix projection;
|
|
// Workaround for software cursor
|
|
// C44Matrix projection(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0.002, 0, -1, -1, 0, 1);
|
|
// this->XformSetProjection(projection);
|
|
|
|
if (!this->StereoEnabled() ||
|
|
(CGxDevice::s_uiVertexShader == 0 || !s_uiVertexShader->Valid()) ||
|
|
(CGxDevice::s_uiPixelShader == 0 || !s_uiPixelShader->Valid())) {
|
|
// Disable shaders
|
|
GxRsSet(GxRs_VertexShader, static_cast<CGxShader*>(nullptr));
|
|
GxRsSet(GxRs_PixelShader, static_cast<CGxShader*>(nullptr));
|
|
} else {
|
|
cursorDepth = this->m_cursorDepth;
|
|
|
|
float minX, maxX, minY, maxY, minZ, maxZ;
|
|
GxXformViewport(minX, maxX, minY, maxY, minZ, maxZ);
|
|
|
|
GxXformProjection(projection);
|
|
|
|
C44Matrix mProj;
|
|
mProj.a0 = 2.0f / (maxX - minX);
|
|
mProj.b0 = 0.0f;
|
|
mProj.c0 = 0.0f;
|
|
mProj.d0 = -((minX + maxX) / (maxX - minX));
|
|
mProj.a1 = 0.0f;
|
|
mProj.b1 = 2.0f / (maxY - minY);
|
|
mProj.c1 = 0.0f;
|
|
mProj.d1 = -((minY + maxY) / (maxY - minY));
|
|
mProj.a2 = 0.0f;
|
|
mProj.b2 = 0.0f;
|
|
mProj.c2 = 1.00008f;
|
|
mProj.d2 = -0.400016f;
|
|
mProj.a3 = 0.0f;
|
|
mProj.b3 = 0.0f;
|
|
mProj.c3 = 1.0f;
|
|
mProj.d3 = 0.0f;
|
|
GxXformSetProjection(mProj);
|
|
|
|
GxRsSet(GxRs_VertexShader, CGxDevice::s_uiVertexShader);
|
|
GxRsSet(GxRs_PixelShader, CGxDevice::s_uiPixelShader);
|
|
|
|
C44Matrix transposition;
|
|
GxXformProjNativeTranspose(transposition);
|
|
GxShaderConstantsSet(GxSh_Vertex, 0, reinterpret_cast<float*>(&transposition), 0);
|
|
}
|
|
|
|
auto buffer = GxBufStream(GxPoolTarget_Vertex, sizeof(CGxVertexPCT), 4);
|
|
auto vertices = reinterpret_cast<CGxVertexPCT*>(GxBufLock(buffer));
|
|
|
|
if (!vertices) {
|
|
return;
|
|
}
|
|
|
|
auto scaleX = this->m_curWindowRect.maxX > 0.0f ? 1.0f / this->m_curWindowRect.maxX : 0.0f;
|
|
auto scaleY = this->m_curWindowRect.maxY > 0.0f ? 1.0f / this->m_curWindowRect.maxY : 0.0f;
|
|
|
|
mouseX -= this->m_cursorHotspotX;
|
|
mouseY -= this->m_cursorHotspotY;
|
|
|
|
auto minX = std::abs(static_cast<float>(mouseX)) * scaleX;
|
|
auto maxX = std::abs(static_cast<float>(mouseX + 32)) * scaleX;
|
|
|
|
auto minY = 1.0f - (std::abs(static_cast<float>(mouseY)) * scaleY);
|
|
auto maxY = 1.0f - (std::abs(static_cast<float>(mouseY + 32)) * scaleY);
|
|
|
|
if (this->m_api == GxApi_D3d9 || this->m_api == GxApi_D3d9Ex) {
|
|
minX -= (scaleX * 0.5f);
|
|
maxX += (scaleX * 0.5f);
|
|
minY -= (scaleY * 0.5f);
|
|
maxY += (scaleY * 0.5f);
|
|
}
|
|
|
|
// Vertex coordinates
|
|
vertices[0].p.x = minX;
|
|
vertices[0].p.y = minY;
|
|
vertices[0].p.z = cursorDepth;
|
|
|
|
vertices[1].p.x = minX;
|
|
vertices[1].p.y = maxY;
|
|
vertices[1].p.z = cursorDepth;
|
|
|
|
vertices[2].p.x = maxX;
|
|
vertices[2].p.y = minY;
|
|
vertices[2].p.z = cursorDepth;
|
|
|
|
vertices[3].p.x = maxX;
|
|
vertices[3].p.y = maxY;
|
|
vertices[3].p.z = cursorDepth;
|
|
|
|
// Color values
|
|
vertices[0].c = { 0xFF, 0xFF, 0xFF, 0xFF };
|
|
vertices[1].c = { 0xFF, 0xFF, 0xFF, 0xFF };
|
|
vertices[2].c = { 0xFF, 0xFF, 0xFF, 0xFF };
|
|
vertices[3].c = { 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
|
// Texture coordinates
|
|
vertices[0].tc[0].x = 0.0f;
|
|
vertices[0].tc[0].y = 0.0f;
|
|
|
|
vertices[1].tc[0].x = 0.0f;
|
|
vertices[1].tc[0].y = 1.0f;
|
|
|
|
vertices[2].tc[0].x = 1.0f;
|
|
vertices[2].tc[0].y = 0.0f;
|
|
|
|
vertices[3].tc[0].x = 1.0f;
|
|
vertices[3].tc[0].y = 1.0f;
|
|
|
|
GxBufUnlock(buffer, 0);
|
|
GxPrimVertexPtr(buffer, GxVBF_PCT);
|
|
|
|
CGxBatch batch;
|
|
batch.m_primType = GxPrim_TriangleStrip;
|
|
batch.m_count = 4;
|
|
batch.m_start = 0;
|
|
batch.m_minIndex = 0;
|
|
batch.m_maxIndex = 3;
|
|
|
|
GxDraw(&batch, 0);
|
|
|
|
GxXformPop(GxXform_World);
|
|
|
|
if (this->StereoEnabled()) {
|
|
GxXformSetProjection(projection);
|
|
}
|
|
|
|
GxRsPop();
|
|
}
|
|
|
|
int32_t CGxDevice::IDevIsWindowed() {
|
|
return this->m_format.window;
|
|
}
|
|
|
|
void CGxDevice::IRsDirty(EGxRenderState which) {
|
|
auto rs = &this->m_appRenderStates[which];
|
|
|
|
if (!rs->m_dirty) {
|
|
auto ds = this->m_dirtyStates.New();
|
|
*ds = which;
|
|
|
|
rs->m_dirty = 1;
|
|
}
|
|
|
|
if (rs->m_stackDepth != this->m_stackOffsets.Count()) {
|
|
auto ps = this->m_pushedStates.New();
|
|
ps->m_which = which;
|
|
ps->m_value = rs->m_value;
|
|
ps->m_stackDepth = rs->m_stackDepth;
|
|
|
|
rs->m_stackDepth = this->m_stackOffsets.Count();
|
|
}
|
|
}
|
|
|
|
void CGxDevice::IRsForceUpdate() {
|
|
for (int32_t which = 0; which < GxRenderStates_Last; which++) {
|
|
auto& rs = this->m_appRenderStates[which];
|
|
auto& hs = this->m_hwRenderStates[which];
|
|
|
|
auto ds = this->m_dirtyStates.New();
|
|
*ds = static_cast<EGxRenderState>(which);
|
|
|
|
rs.m_dirty = 1;
|
|
|
|
// TODO manage this through operators
|
|
hs.m_data.i[0] = ~rs.m_value.m_data.i[0];
|
|
hs.m_data.i[1] = ~rs.m_value.m_data.i[1];
|
|
hs.m_data.i[2] = ~rs.m_value.m_data.i[2];
|
|
hs.filler = ~rs.m_value.filler;
|
|
}
|
|
}
|
|
|
|
void CGxDevice::IRsForceUpdate(EGxRenderState which) {
|
|
if (!this->m_context) {
|
|
return;
|
|
}
|
|
|
|
auto& rs = this->m_appRenderStates[which];
|
|
auto& hs = this->m_hwRenderStates[which];
|
|
|
|
auto ds = this->m_dirtyStates.New();
|
|
*ds = which;
|
|
|
|
rs.m_dirty = 1;
|
|
|
|
// TODO manage this through operators
|
|
hs.m_data.i[0] = ~rs.m_value.m_data.i[0];
|
|
hs.m_data.i[1] = ~rs.m_value.m_data.i[1];
|
|
hs.m_data.i[2] = ~rs.m_value.m_data.i[2];
|
|
hs.filler = ~rs.m_value.filler;
|
|
}
|
|
|
|
void CGxDevice::IRsInit() {
|
|
this->m_appRenderStates.SetCount(GxRenderStates_Last);
|
|
this->m_hwRenderStates.SetCount(GxRenderStates_Last);
|
|
|
|
memset(this->m_appRenderStates.m_data, 0, sizeof(CGxAppRenderState) * GxRenderStates_Last);
|
|
memset(this->m_hwRenderStates.m_data, 0, sizeof(CGxStateBom) * GxRenderStates_Last);
|
|
|
|
this->m_appRenderStates[GxRs_PolygonOffset].m_value = 0;
|
|
this->m_appRenderStates[GxRs_MatDiffuse].m_value = 0xFFFFFFFF;
|
|
this->m_appRenderStates[GxRs_MatEmissive].m_value = 0;
|
|
this->m_appRenderStates[GxRs_MatSpecular].m_value = 0;
|
|
this->m_appRenderStates[GxRs_MatSpecularExp].m_value = 0.0f;
|
|
this->m_appRenderStates[GxRs_NormalizeNormals].m_value = 0;
|
|
this->m_appRenderStates[GxRs_BlendingMode].m_value = GxBlend_Opaque;
|
|
this->m_appRenderStates[GxRs_FogStart].m_value = 1.0f;
|
|
this->m_appRenderStates[GxRs_FogEnd].m_value = 0.0f;
|
|
this->m_appRenderStates[GxRs_FogColor].m_value = 0xFF808080;
|
|
this->m_appRenderStates[GxRs_Lighting].m_value = 1;
|
|
this->m_appRenderStates[GxRs_Fog].m_value = 1;
|
|
this->m_appRenderStates[GxRs_DepthTest].m_value = 1;
|
|
this->m_appRenderStates[GxRs_DepthFunc].m_value = 0;
|
|
this->m_appRenderStates[GxRs_DepthWrite].m_value = 1;
|
|
this->m_appRenderStates[GxRs_ColorWrite].m_value = 15;
|
|
this->m_appRenderStates[GxRs_Culling].m_value = 1;
|
|
this->m_appRenderStates[GxRs_ClipPlaneMask].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Multisample].m_value = 1;
|
|
this->m_appRenderStates[GxRs_ScissorTest].m_value = 0;
|
|
|
|
this->m_appRenderStates[GxRs_Texture0].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_ColorOp0].m_value = 0;
|
|
this->m_appRenderStates[GxRs_AlphaOp0].m_value = 0;
|
|
this->m_appRenderStates[GxRs_TexGen0].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk61].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk69].m_value = 0;
|
|
|
|
this->m_appRenderStates[GxRs_Texture1].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_ColorOp1].m_value = 0;
|
|
this->m_appRenderStates[GxRs_AlphaOp1].m_value = 0;
|
|
this->m_appRenderStates[GxRs_TexGen1].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk62].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk70].m_value = 1;
|
|
|
|
this->m_appRenderStates[GxRs_Texture2].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_ColorOp2].m_value = 0;
|
|
this->m_appRenderStates[GxRs_AlphaOp2].m_value = 0;
|
|
this->m_appRenderStates[GxRs_TexGen2].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk63].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk71].m_value = 2;
|
|
|
|
this->m_appRenderStates[GxRs_Texture3].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_ColorOp3].m_value = 0;
|
|
this->m_appRenderStates[GxRs_AlphaOp3].m_value = 0;
|
|
this->m_appRenderStates[GxRs_TexGen3].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk64].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk72].m_value = 3;
|
|
|
|
this->m_appRenderStates[GxRs_Texture4].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_ColorOp4].m_value = 0;
|
|
this->m_appRenderStates[GxRs_AlphaOp4].m_value = 0;
|
|
this->m_appRenderStates[GxRs_TexGen4].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk65].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk73].m_value = 4;
|
|
|
|
this->m_appRenderStates[GxRs_Texture5].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_ColorOp5].m_value = 0;
|
|
this->m_appRenderStates[GxRs_AlphaOp5].m_value = 0;
|
|
this->m_appRenderStates[GxRs_TexGen5].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk66].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk74].m_value = 5;
|
|
|
|
this->m_appRenderStates[GxRs_Texture6].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_ColorOp6].m_value = 0;
|
|
this->m_appRenderStates[GxRs_AlphaOp6].m_value = 0;
|
|
this->m_appRenderStates[GxRs_TexGen6].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk67].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk75].m_value = 6;
|
|
|
|
this->m_appRenderStates[GxRs_Texture7].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_ColorOp7].m_value = 0;
|
|
this->m_appRenderStates[GxRs_AlphaOp7].m_value = 0;
|
|
this->m_appRenderStates[GxRs_TexGen7].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk68].m_value = 0;
|
|
this->m_appRenderStates[GxRs_Unk76].m_value = 7;
|
|
|
|
this->m_appRenderStates[GxRs_Texture8].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_Texture9].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_Texture10].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_Texture11].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_Texture12].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_Texture13].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_Texture14].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_Texture15].m_value = nullptr;
|
|
|
|
this->m_appRenderStates[GxRs_PixelShader].m_value = nullptr;
|
|
this->m_appRenderStates[GxRs_VertexShader].m_value = nullptr;
|
|
|
|
this->m_appRenderStates[GxRs_PointScale].m_value = 1.0f;
|
|
this->m_appRenderStates[GxRs_PointScaleAttenuation].m_value = CGxDevice::s_pointScaleIdentity;
|
|
this->m_appRenderStates[GxRs_PointScaleMin].m_value = 0.0f;
|
|
this->m_appRenderStates[GxRs_PointScaleMax].m_value = 1.0f;
|
|
this->m_appRenderStates[GxRs_PointSprite].m_value = 0;
|
|
|
|
this->m_appRenderStates[GxRs_Unk84].m_value = 0.0f;
|
|
this->m_appRenderStates[GxRs_ColorMaterial].m_value = 0;
|
|
}
|
|
|
|
void CGxDevice::IRsSync(int32_t force) {
|
|
if (force) {
|
|
this->IRsForceUpdate();
|
|
}
|
|
|
|
for (int32_t i = this->m_dirtyStates.Count() - 1; i >= 0; i--) {
|
|
auto ds = this->m_dirtyStates[i];
|
|
auto rs = &this->m_appRenderStates[ds];
|
|
auto hs = &this->m_hwRenderStates[ds];
|
|
|
|
if (rs->m_dirty && rs->m_value != *hs) {
|
|
this->IRsSendToHw(ds);
|
|
}
|
|
|
|
*hs = rs->m_value;
|
|
rs->m_dirty = 0;
|
|
}
|
|
|
|
this->m_dirtyStates.SetCount(0);
|
|
}
|
|
|
|
void CGxDevice::IShaderLoad(CGxShader* shaders[], EGxShTarget target, const char* a4, const char* a5, int32_t permutations) {
|
|
int32_t profile = this->m_caps.m_shaderTargets[target];
|
|
|
|
if (!profile) {
|
|
return;
|
|
}
|
|
|
|
char path[260];
|
|
SFile* file = nullptr;
|
|
|
|
while (true) {
|
|
sprintf(path, "%s\\%s\\%s.bls", a4, g_gxShaderProfileNames[target][profile], a5);
|
|
SFile::Open(path, &file);
|
|
|
|
// Opened shader file
|
|
if (file) {
|
|
break;
|
|
}
|
|
|
|
// Fallback
|
|
if (target == GxSh_Vertex) {
|
|
// vs_3_0 -> vs_2_0
|
|
// vs_2_0 -> vs_1_1
|
|
|
|
if (profile == 3) {
|
|
profile = 2;
|
|
} else if (profile == 2) {
|
|
profile = 1;
|
|
} else {
|
|
break;
|
|
}
|
|
} else if (target == GxSh_Pixel) {
|
|
// nvts3 -> nvts
|
|
// nvts2 -> nvts
|
|
// ps_3_0 -> ps_2_0
|
|
// ps_2_0 -> ps_1_4
|
|
// ps_1_4 -> ps_1_1
|
|
|
|
if (profile == 9 || profile == 10) {
|
|
profile = 8;
|
|
} else if (profile == 4) {
|
|
profile = 3;
|
|
} else if (profile == 3) {
|
|
profile = 2;
|
|
} else if (profile == 2) {
|
|
profile = 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!file) {
|
|
return;
|
|
}
|
|
|
|
uint32_t signature;
|
|
uint32_t version;
|
|
uint32_t permutationCount;
|
|
|
|
if (file) {
|
|
SFile::Read(file, &signature, 4, nullptr, nullptr, nullptr);
|
|
|
|
if (signature == 'GXSH') {
|
|
SFile::Read(file, &version, 4, nullptr, nullptr, nullptr);
|
|
|
|
if (version == 0x10003) {
|
|
SFile::Read(file, &permutationCount, 4, nullptr, nullptr, nullptr);
|
|
|
|
// TODO
|
|
// assert(permutationCount == permutations);
|
|
|
|
if (permutations > 0) {
|
|
for (int32_t p = 0; p < permutations; p++) {
|
|
shaders[p]->Load(file);
|
|
|
|
shaders[p]->loaded = 0;
|
|
shaders[p]->int34 = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SFile::Close(file);
|
|
}
|
|
}
|
|
|
|
void CGxDevice::ITexMarkAsUpdated(CGxTex* texId) {
|
|
if (!texId->m_needsUpdate) {
|
|
return;
|
|
}
|
|
|
|
texId->m_updateRect = {
|
|
static_cast<int32_t>(texId->m_height),
|
|
static_cast<int32_t>(texId->m_width),
|
|
0,
|
|
0
|
|
};
|
|
|
|
texId->m_needsUpdate = 0;
|
|
}
|
|
|
|
void CGxDevice::ITexWHDStartEnd(CGxTex* texId, uint32_t& width, uint32_t& height, uint32_t& baseMip, uint32_t& mipCount) {
|
|
width = texId->m_width;
|
|
height = texId->m_height;
|
|
|
|
if ((texId->m_flags.m_filter == GxTex_Nearest || texId->m_flags.m_filter == GxTex_Linear || texId->m_flags.m_generateMipMaps) && !texId->m_flags.m_forceMipTracking) {
|
|
baseMip = 0;
|
|
mipCount = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
mipCount = 1;
|
|
|
|
if (texId->m_format == GxTex_Dxt1 || texId->m_format == GxTex_Dxt3 || texId->m_format == GxTex_Dxt5) {
|
|
uint32_t shortEdge = std::min(texId->m_width, texId->m_height);
|
|
|
|
while (shortEdge > 4) {
|
|
shortEdge /= 2;
|
|
mipCount++;
|
|
}
|
|
} else {
|
|
uint32_t longEdge = std::max(texId->m_width, texId->m_height);
|
|
|
|
while (longEdge > 1) {
|
|
longEdge /= 2;
|
|
mipCount++;
|
|
}
|
|
}
|
|
|
|
baseMip = std::min(this->m_baseMipLevel, mipCount - 1);
|
|
|
|
width >>= baseMip;
|
|
height >>= baseMip;
|
|
|
|
if (texId->m_flags.m_forceMipTracking) {
|
|
mipCount = baseMip + 1;
|
|
}
|
|
}
|
|
|
|
int32_t CGxDevice::MasterEnable(EGxMasterEnables state) {
|
|
return ((1 << state) & this->m_appMasterEnables) != 0;
|
|
}
|
|
|
|
void CGxDevice::MasterEnableSet(EGxMasterEnables state, int32_t enable) {
|
|
this->m_appMasterEnables = ((enable & 1) << state) | (this->m_appMasterEnables & ~(1 << state));
|
|
|
|
switch (state) {
|
|
case GxMasterEnable_Lighting:
|
|
this->IRsForceUpdate(GxRs_Lighting);
|
|
break;
|
|
|
|
case GxMasterEnable_Fog:
|
|
this->IRsForceUpdate(GxRs_Fog);
|
|
break;
|
|
|
|
case GxMasterEnable_DepthTest:
|
|
this->IRsForceUpdate(GxRs_DepthTest);
|
|
break;
|
|
|
|
case GxMasterEnable_DepthWrite:
|
|
this->IRsForceUpdate(GxRs_DepthWrite);
|
|
break;
|
|
|
|
case GxMasterEnable_ColorWrite:
|
|
this->IRsForceUpdate(GxRs_ColorWrite);
|
|
break;
|
|
|
|
case GxMasterEnable_Culling:
|
|
this->IRsForceUpdate(GxRs_Culling);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CGxDevice::PrimIndexPtr(CGxBuf* buf) {
|
|
if (buf->unk1E || this->m_primIndexBuf != buf) {
|
|
buf->unk1E = 0;
|
|
this->m_primIndexDirty = 1;
|
|
this->m_primIndexBuf = buf;
|
|
}
|
|
}
|
|
|
|
void CGxDevice::PrimVertexFormat(CGxBuf* buf, CGxVertexAttrib* attribs, uint32_t count) {
|
|
for (int32_t i = 0; i < count; i++) {
|
|
int32_t attrib = attribs->attrib;
|
|
|
|
int32_t dirty = buf->unk1E
|
|
|| this->m_primVertexFormatBuf[attrib] != buf
|
|
|| this->m_primVertexFormatAttrib[attrib].type != attribs->type
|
|
|| this->m_primVertexFormatAttrib[attrib].offset != attribs->offset
|
|
|| this->m_primVertexFormatAttrib[attrib].bufSize != attribs->bufSize;
|
|
|
|
if (dirty) {
|
|
this->m_primVertexDirty |= 1 << attrib;
|
|
}
|
|
|
|
this->m_primVertexFormatBuf[attrib] = buf;
|
|
|
|
this->m_primVertexFormatAttrib[attrib].attrib = attribs->attrib;
|
|
this->m_primVertexFormatAttrib[attrib].type = attribs->type;
|
|
this->m_primVertexFormatAttrib[attrib].offset = attribs->offset;
|
|
this->m_primVertexFormatAttrib[attrib].bufSize = attribs->bufSize;
|
|
|
|
attribs++;
|
|
}
|
|
|
|
buf->unk1E = 0;
|
|
this->m_primVertexFormat = GxVertexBufferFormats_Last;
|
|
}
|
|
|
|
void CGxDevice::PrimVertexMask(uint32_t mask) {
|
|
this->m_primVertexDirty |= mask ^ this->m_primVertexMask;
|
|
this->m_primVertexMask = mask;
|
|
this->m_primVertexFormat = GxVertexBufferFormats_Last;
|
|
}
|
|
|
|
void CGxDevice::PrimVertexPtr(CGxBuf* buf, EGxVertexBufferFormat format) {
|
|
this->m_primVertexFormat = format;
|
|
this->m_primVertexBuf = buf;
|
|
this->m_primVertexSize = Buffer::s_vertexBufDesc[format].size;
|
|
}
|
|
|
|
CGxPool* CGxDevice::PoolCreate(EGxPoolTarget target, EGxPoolUsage usage, uint32_t size, EGxPoolHintBits hint, const char* name) {
|
|
auto m = SMemAlloc(sizeof(CGxPool), __FILE__, __LINE__, 0x0);
|
|
auto pool = new (m) CGxPool(target, usage, size, hint, name);
|
|
|
|
this->m_poolList.LinkToTail(pool);
|
|
|
|
return pool;
|
|
}
|
|
|
|
void CGxDevice::RsGet(EGxRenderState which, int32_t& value) {
|
|
value = static_cast<int32_t>(this->m_appRenderStates[which].m_value);
|
|
}
|
|
|
|
void CGxDevice::RsSet(EGxRenderState which, int32_t value) {
|
|
if (!this->m_context) {
|
|
return;
|
|
}
|
|
|
|
if (this->m_appRenderStates[which].m_value != value) {
|
|
this->IRsDirty(which);
|
|
this->m_appRenderStates[which].m_value = value;
|
|
}
|
|
}
|
|
|
|
void CGxDevice::RsSet(EGxRenderState which, void* value) {
|
|
if (!this->m_context) {
|
|
return;
|
|
}
|
|
|
|
if (this->m_appRenderStates[which].m_value != value) {
|
|
this->IRsDirty(which);
|
|
this->m_appRenderStates[which].m_value = value;
|
|
|
|
if (value) {
|
|
if (which >= GxRs_Texture0 && which <= GxRs_Texture15) {
|
|
CGxTex* texture = static_cast<CGxTex*>(value);
|
|
|
|
if (texture->m_flags.m_renderTarget && texture->m_needsUpdate) {
|
|
this->ITexMarkAsUpdated(texture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGxDevice::RsSetAlphaRef() {
|
|
if (!this->m_context) {
|
|
return;
|
|
}
|
|
|
|
int32_t blendingMode;
|
|
this->RsGet(GxRs_BlendingMode, blendingMode);
|
|
|
|
this->RsSet(GxRs_AlphaRef, CGxDevice::s_alphaRef[blendingMode]);
|
|
}
|
|
|
|
void CGxDevice::RsPop() {
|
|
auto topOfStack = this->m_stackOffsets[this->m_stackOffsets.Count() - 1];
|
|
|
|
if (this->m_pushedStates.Count() > topOfStack) {
|
|
auto bottomOfStack = this->m_pushedStates.Count() - 1;
|
|
auto stackSize = this->m_pushedStates.Count() - topOfStack;
|
|
|
|
for (uint32_t stackOffset = 0; stackOffset < stackSize; stackOffset++) {
|
|
auto ps = &this->m_pushedStates[bottomOfStack - stackOffset];
|
|
auto rs = &this->m_appRenderStates[ps->m_which];
|
|
|
|
if (!rs->m_dirty) {
|
|
auto ds = this->m_dirtyStates.New();
|
|
*ds = ps->m_which;
|
|
|
|
rs->m_dirty = 1;
|
|
}
|
|
|
|
rs->m_value = ps->m_value;
|
|
rs->m_stackDepth = ps->m_stackDepth;
|
|
}
|
|
}
|
|
|
|
this->m_pushedStates.SetCount(topOfStack);
|
|
this->m_stackOffsets.SetCount(this->m_stackOffsets.Count() - 1);
|
|
}
|
|
|
|
void CGxDevice::RsPush() {
|
|
auto offset = this->m_stackOffsets.New();
|
|
*offset = this->m_pushedStates.Count();
|
|
}
|
|
|
|
void CGxDevice::ScenePresent() {
|
|
// TODO
|
|
}
|
|
|
|
void CGxDevice::ShaderConstantsClear() {
|
|
for (int32_t i = 0; i < 256; i++) {
|
|
CGxDevice::s_shadowConstants[0].constants[i] = {
|
|
std::numeric_limits<float>::max(),
|
|
std::numeric_limits<float>::max(),
|
|
std::numeric_limits<float>::max(),
|
|
std::numeric_limits<float>::max()
|
|
};
|
|
|
|
CGxDevice::s_shadowConstants[1].constants[i] = {
|
|
std::numeric_limits<float>::max(),
|
|
std::numeric_limits<float>::max(),
|
|
std::numeric_limits<float>::max(),
|
|
std::numeric_limits<float>::max()
|
|
};
|
|
}
|
|
|
|
CGxDevice::s_shadowConstants[0].unk1 = 0;
|
|
CGxDevice::s_shadowConstants[0].unk2 = 255;
|
|
|
|
CGxDevice::s_shadowConstants[1].unk1 = 0;
|
|
CGxDevice::s_shadowConstants[1].unk2 = 255;
|
|
}
|
|
|
|
char* CGxDevice::ShaderConstantsLock(EGxShTarget target) {
|
|
return target == GxSh_Vertex
|
|
? reinterpret_cast<char*>(&CGxDevice::s_shadowConstants[1].constants)
|
|
: reinterpret_cast<char*>(&CGxDevice::s_shadowConstants[0].constants);
|
|
}
|
|
|
|
void CGxDevice::ShaderConstantsSet(EGxShTarget target, uint32_t index, const float* constants, uint32_t count) {
|
|
STORM_ASSERT((index + count - 1) <= 255);
|
|
|
|
if (!count) {
|
|
return;
|
|
}
|
|
|
|
ShaderConstants* dst;
|
|
|
|
if (target == GxSh_Vertex) {
|
|
dst = &CGxDevice::s_shadowConstants[1];
|
|
} else if (target == GxSh_Pixel) {
|
|
dst = &CGxDevice::s_shadowConstants[0];
|
|
} else {
|
|
STORM_ASSERT(false);
|
|
}
|
|
|
|
const float* c = constants;
|
|
|
|
for (int32_t i = index; i < index + count; i++, c += 4) {
|
|
int32_t dirty = 0;
|
|
|
|
if (dst->constants[i].x != c[0]) {
|
|
dirty = 1;
|
|
dst->constants[i].x = c[0];
|
|
}
|
|
|
|
if (dst->constants[i].y != c[1]) {
|
|
dirty = 1;
|
|
dst->constants[i].y = c[1];
|
|
}
|
|
|
|
if (dst->constants[i].z != c[2]) {
|
|
dirty = 1;
|
|
dst->constants[i].z = c[2];
|
|
}
|
|
|
|
if (dst->constants[i].w != c[3]) {
|
|
dirty = 1;
|
|
dst->constants[i].w = c[3];
|
|
}
|
|
|
|
if (dirty) {
|
|
if (dst->unk2 > i) {
|
|
dst->unk2 = i;
|
|
}
|
|
|
|
if (dst->unk1 < i) {
|
|
dst->unk1 = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGxDevice::ShaderConstantsUnlock(EGxShTarget target, uint32_t index, uint32_t count) {
|
|
if (target == GxSh_Pixel) {
|
|
ShaderConstants& dst = CGxDevice::s_shadowConstants[0];
|
|
dst.unk2 = std::min(dst.unk2, index);
|
|
dst.unk1 = std::max(dst.unk1, index + count - 1);
|
|
} else {
|
|
ShaderConstants& dst = CGxDevice::s_shadowConstants[1];
|
|
dst.unk2 = std::min(dst.unk2, index);
|
|
dst.unk1 = std::max(dst.unk1, index + count - 1);
|
|
}
|
|
}
|
|
|
|
void CGxDevice::ShaderCreate(CGxShader* shaders[], EGxShTarget target, const char* a4, const char* a5, int32_t permutations) {
|
|
auto shaderList = &this->m_shaderList[target];
|
|
|
|
if (permutations == 0) {
|
|
return;
|
|
}
|
|
|
|
if (permutations == 1) {
|
|
auto shader = shaderList->Ptr(a5);
|
|
|
|
if (shader) {
|
|
shaders[0] = shader;
|
|
shader->refCount++;
|
|
return;
|
|
}
|
|
|
|
shader = shaderList->New(a5, 0, 0);
|
|
shaders[0] = shader;
|
|
|
|
shader->refCount++;
|
|
shader->target = target;
|
|
|
|
this->IShaderLoad(shaders, target, a4, a5, permutations);
|
|
|
|
return;
|
|
}
|
|
|
|
memset(shaders, 0, permutations * sizeof(void*));
|
|
|
|
char key[256];
|
|
|
|
int32_t p = 0;
|
|
|
|
while (p < permutations) {
|
|
sprintf(key, "%s:%d", a5, p);
|
|
|
|
auto shader = shaderList->Ptr(key);
|
|
shaders[p] = shader;
|
|
|
|
if (!shader) {
|
|
break;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
|
|
// Fully loaded
|
|
if (p == permutations) {
|
|
for (p = 0; p < permutations; p++) {
|
|
shaders[p]->refCount++;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
for (p = 0; p < permutations; p++) {
|
|
sprintf(key, "%s:%d", a5, p);
|
|
|
|
auto shader = shaderList->New(key, 0, 0);
|
|
shaders[p] = shader;
|
|
|
|
shader->refCount++;
|
|
shader->target = target;
|
|
}
|
|
|
|
this->IShaderLoad(shaders, target, a4, a5, permutations);
|
|
}
|
|
|
|
int32_t CGxDevice::TexCreate(EGxTexTarget target, uint32_t width, uint32_t height, uint32_t depth, EGxTexFormat format, EGxTexFormat dataFormat, CGxTexFlags flags, void* userArg, void (*userFunc)(EGxTexCommand, uint32_t, uint32_t, uint32_t, uint32_t, void*, uint32_t&, const void*&), const char* name, CGxTex*& texId) {
|
|
auto m = SMemAlloc(sizeof(CGxTex), __FILE__, __LINE__, 0);
|
|
auto tex = new (m) CGxTex(
|
|
target,
|
|
width,
|
|
height,
|
|
depth,
|
|
format,
|
|
dataFormat,
|
|
flags,
|
|
userArg,
|
|
userFunc,
|
|
name
|
|
);
|
|
|
|
texId = tex;
|
|
|
|
// TODO
|
|
// - link tex to list in device
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CGxDevice::TexDestroy(CGxTex* texId) {
|
|
// TODO
|
|
|
|
if (texId) {
|
|
delete texId;
|
|
}
|
|
}
|
|
|
|
void CGxDevice::TexMarkForUpdate(CGxTex* texId, const CiRect& updateRect, int32_t immediate) {
|
|
texId->m_needsUpdate = 1;
|
|
|
|
// If the bounds of the updateRect are invalid, default to { 0, 0, height, width }
|
|
if (updateRect.minY >= updateRect.maxY || updateRect.minX >= updateRect.maxX) {
|
|
texId->m_updateRect = {
|
|
0,
|
|
0,
|
|
static_cast<int32_t>(texId->m_height),
|
|
static_cast<int32_t>(texId->m_width)
|
|
};
|
|
} else {
|
|
texId->m_updateRect = updateRect;
|
|
}
|
|
|
|
if (immediate) {
|
|
this->ITexMarkAsUpdated(texId);
|
|
}
|
|
}
|
|
|
|
void CGxDevice::TexSetWrap(CGxTex* texId, EGxTexWrapMode wrapU, EGxTexWrapMode wrapV) {
|
|
if (texId->m_flags.m_wrapU == wrapU && texId->m_flags.m_wrapV == wrapV) {
|
|
return;
|
|
}
|
|
|
|
texId->m_flags.m_wrapU = wrapU;
|
|
texId->m_flags.m_wrapV = wrapV;
|
|
texId->m_needsFlagUpdate = 1;
|
|
|
|
for (int32_t rs = GxRs_Texture0; rs <= GxRs_Texture15; rs++) {
|
|
this->IRsForceUpdate(static_cast<EGxRenderState>(rs));
|
|
}
|
|
}
|
|
|
|
void CGxDevice::ValidateDraw(CGxBatch* batch, int32_t count) {
|
|
// TODO
|
|
}
|
|
|
|
void CGxDevice::XformPop(EGxXform xf) {
|
|
this->m_xforms[xf].Pop();
|
|
}
|
|
|
|
void CGxDevice::XformProjection(C44Matrix& matrix) {
|
|
matrix = this->m_projection;
|
|
}
|
|
|
|
void CGxDevice::XformProjNative(C44Matrix& matrix) {
|
|
matrix = this->m_projNative;
|
|
|
|
if (this->m_api == GxApi_OpenGl) {
|
|
matrix.c0 *= -1.0f;
|
|
matrix.c1 *= -1.0f;
|
|
matrix.c2 *= -1.0f;
|
|
matrix.c3 *= -1.0f;
|
|
}
|
|
}
|
|
|
|
void CGxDevice::XformPush(EGxXform xf) {
|
|
this->m_xforms[xf].Push();
|
|
}
|
|
|
|
// 1-liner for Push/Set
|
|
void CGxDevice::XformPush(EGxXform xf, const C44Matrix& matrix) {
|
|
this->m_xforms[xf].Push();
|
|
this->m_xforms[xf].Top() = matrix;
|
|
}
|
|
|
|
void CGxDevice::XformSet(EGxXform xf, const C44Matrix& matrix) {
|
|
this->m_xforms[xf].Top() = matrix;
|
|
}
|
|
|
|
void CGxDevice::XformSetProjection(const C44Matrix& matrix) {
|
|
this->m_projection = matrix;
|
|
}
|
|
|
|
void CGxDevice::XformSetView(const C44Matrix& matrix) {
|
|
this->m_xforms[GxXform_View].Top() = matrix;
|
|
|
|
for (int32_t i = GxRs_TexGen0; i < GxRs_TexGen7; i++) {
|
|
if (static_cast<int32_t>(this->m_appRenderStates[i].m_value) - 1 <= 1) {
|
|
this->IRsForceUpdate(static_cast<EGxRenderState>(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGxDevice::XformSetViewport(float minX, float maxX, float minY, float maxY, float minZ, float maxZ) {
|
|
if (
|
|
minX == this->m_viewport.x.l
|
|
&& maxX == this->m_viewport.x.h
|
|
&& minY == this->m_viewport.y.l
|
|
&& maxY == this->m_viewport.y.h
|
|
&& minZ == this->m_viewport.z.l
|
|
&& maxZ == this->m_viewport.z.h
|
|
) {
|
|
return;
|
|
}
|
|
|
|
this->intF6C = 1;
|
|
|
|
this->m_viewport.x.l = minX;
|
|
this->m_viewport.x.h = maxX;
|
|
this->m_viewport.y.l = minY;
|
|
this->m_viewport.y.h = maxY;
|
|
this->m_viewport.z.l = minZ;
|
|
this->m_viewport.z.h = maxZ;
|
|
}
|
|
|
|
void CGxDevice::XformView(C44Matrix& matrix) {
|
|
matrix = this->m_xforms[GxXform_View].m_mtx[this->m_xforms[GxXform_View].m_level];
|
|
}
|
|
|
|
void CGxDevice::XformViewport(float& minX, float& maxX, float& minY, float& maxY, float& minZ, float& maxZ) {
|
|
minX = this->m_viewport.x.l;
|
|
maxX = this->m_viewport.x.h;
|
|
minY = this->m_viewport.y.l;
|
|
maxY = this->m_viewport.y.h;
|
|
minZ = this->m_viewport.z.l;
|
|
maxZ = this->m_viewport.z.h;
|
|
}
|
|
|
|
void CGxDevice::CursorSetVisible(int32_t visible) {
|
|
this->m_cursorVisible = visible;
|
|
}
|
|
|
|
uint32_t* CGxDevice::CursorLock() {
|
|
return this->m_cursor;
|
|
}
|
|
|
|
void CGxDevice::CursorUnlock(uint32_t x, uint32_t y) {
|
|
this->m_cursorHotspotX = x;
|
|
this->m_cursorHotspotY = y;
|
|
}
|