Implemented drawing plugins by moving HLE and LLE push buffer drawing into separate functions and passing HLE pushbuffer command handling on to the LLE pgraph method handler.
Also removed a few unused symbols (X_D3DRESOURCE_LOCK_PALETTE, extern EmuUpdateActiveTextureStages, g_bBadIndexData)
This commit is contained in:
parent
b46a82b23a
commit
7ccd90e2bd
|
@ -800,9 +800,6 @@ XTL::IDirect3DResource *GetHostResource(XTL::X_D3DResource *pXboxResource, DWORD
|
|||
|
||||
EmuVerifyResourceIsRegistered(pXboxResource, D3DUsage, iTextureStage, /*dwSize=*/0);
|
||||
|
||||
if (pXboxResource->Lock == X_D3DRESOURCE_LOCK_PALETTE)
|
||||
return nullptr;
|
||||
|
||||
auto key = GetHostResourceKey(pXboxResource);
|
||||
auto it = g_XboxDirect3DResources.find(key);
|
||||
if (it == g_XboxDirect3DResources.end() || !it->second.pHostResource) {
|
||||
|
@ -861,8 +858,7 @@ bool HostResourceRequiresUpdate(resource_key_t key, DWORD dwSize)
|
|||
|
||||
// Currently, we only dynamically update Textures and Surfaces, so if our resource
|
||||
// isn't of these types, do nothing
|
||||
DWORD type = GetXboxCommonResourceType(it->second.pXboxResource);
|
||||
if (type != X_D3DCOMMON_TYPE_SURFACE && type != X_D3DCOMMON_TYPE_TEXTURE) {
|
||||
if (!IsResourceAPixelContainer(it->second.pXboxResource)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1022,6 +1018,7 @@ void SetHostIndexBuffer(XTL::X_D3DResource *pXboxResource, XTL::IDirect3DIndexBu
|
|||
|
||||
SetHostResource(pXboxResource, (XTL::IDirect3DResource*)pHostIndexBuffer);
|
||||
}
|
||||
|
||||
int XboxD3DPaletteSizeToBytes(const XTL::X_D3DPALETTESIZE Size)
|
||||
{
|
||||
static int lk[4] =
|
||||
|
@ -2360,12 +2357,12 @@ static void EmuVerifyResourceIsRegistered(XTL::X_D3DResource *pResource, DWORD D
|
|||
//check if the same key existed in the HostResource map already. if there is a old pXboxResource in the map with the same key but different resource address, it must be freed first.
|
||||
|
||||
if (it->second.pXboxResource != pResource) {
|
||||
//printf("EmuVerifyResourceIsRegistered passed in XboxResource collipse HostResource map!! key : %llX , map pXboxResource : %08X , passed in pResource : %08X \n", key, it->second.pXboxResource, pResource);
|
||||
FreeHostResource(key);
|
||||
//printf("EmuVerifyResourceIsRegistered passed in XboxResource collides HostResource map!! key : %llX , map pXboxResource : %08X , passed in pResource : %08X \n", key, it->second.pXboxResource, pResource);
|
||||
}
|
||||
else
|
||||
if (!HostResourceRequiresUpdate(key, dwSize)) {
|
||||
return;
|
||||
else {
|
||||
if (!HostResourceRequiresUpdate(key, dwSize)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FreeHostResource(key);
|
||||
|
@ -3784,8 +3781,6 @@ VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInlineFast)
|
|||
EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register, pConstantData, ConstantCount / 4);
|
||||
}
|
||||
|
||||
BOOL g_bBadIndexData = FALSE;
|
||||
|
||||
// LTCG specific D3DDevice_SetTexture function...
|
||||
// This uses a custom calling convention where parameter is passed in EAX
|
||||
// TODO: XB_trampoline plus Log function is not working due lost parameter in EAX.
|
||||
|
@ -7925,9 +7920,9 @@ XTL::D3DCOLOR * WINAPI XTL::EMUPATCH(D3DPalette_Lock2)
|
|||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(pThis)
|
||||
LOG_FUNC_ARG(Flags)
|
||||
LOG_FUNC_END;
|
||||
LOG_FUNC_ARG(pThis)
|
||||
LOG_FUNC_ARG(Flags)
|
||||
LOG_FUNC_END;
|
||||
|
||||
XB_trampoline(XTL::D3DCOLOR*, WINAPI, D3DPalette_Lock2, (X_D3DPalette*, DWORD));
|
||||
XTL::D3DCOLOR* pData = XB_D3DPalette_Lock2(pThis, Flags);
|
||||
|
|
|
@ -561,7 +561,6 @@ struct X_D3DResource
|
|||
|
||||
// special resource lock flags
|
||||
#define X_D3DRESOURCE_LOCK_FLAG_NOSIZE 0xEFFFFFFF
|
||||
#define X_D3DRESOURCE_LOCK_PALETTE 0x8000BEEF
|
||||
|
||||
// Lock flags
|
||||
#define X_D3DLOCK_NOFLUSH 0x00000010 // Xbox extension
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#include "CxbxKrnl/EmuXTL.h"
|
||||
#include "XbD3D8Types.h" // For X_D3DFORMAT
|
||||
#include "CxbxKrnl/ResourceTracker.h"
|
||||
#include "devices/video/nv2a.h" // For PGRAPHState
|
||||
#include "devices/video/nv2a.h" // For g_NV2A, PGRAPHState
|
||||
#include "devices/video/nv2a_int.h" // For NV** defines
|
||||
#include "Logging.h"
|
||||
|
||||
|
@ -212,294 +212,133 @@ DWORD CxbxGetStrideFromVertexShaderHandle(DWORD dwVertexShader)
|
|||
return Stride;
|
||||
}
|
||||
|
||||
PGRAPHState pgraph_state = {}; // global, as inside a function it crashes (during initalization?)
|
||||
|
||||
void HLE_pgraph_handle_method(
|
||||
PGRAPHState *pg, // compatiblity, instead of NV2AState *d,
|
||||
unsigned int subchannel,
|
||||
unsigned int method,
|
||||
uint32_t parameter)
|
||||
void HLE_draw_arrays(NV2AState *d)
|
||||
{
|
||||
// PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
using namespace XTL;
|
||||
|
||||
LOG_INIT // Allows use of DEBUG_D3DRESULT
|
||||
LOG_TEST_CASE("HLE_draw_arrays");
|
||||
|
||||
// Skip all commands not intended for channel 0 (3D)
|
||||
if (subchannel > 0) {
|
||||
LOG_TEST_CASE("Pushbuffer subchannel > 0");
|
||||
return; // For now, don't even attempt to run through
|
||||
}
|
||||
|
||||
#if 1 // Temporarily, use this array of 16 bit elements (until HLE drawing uses 32 bit indices, like LLE)
|
||||
static INDEX16 pg__inline_elements_16[NV2A_MAX_BATCH_LENGTH];
|
||||
#define pg__inline_elements pg__inline_elements_16
|
||||
#else
|
||||
#define pg__inline_elements pg->inline_elements
|
||||
#endif
|
||||
|
||||
switch (method) {
|
||||
|
||||
case 0: {
|
||||
LOG_TEST_CASE("Pushbuffer method == 0");
|
||||
break;
|
||||
}
|
||||
case NV097_NO_OPERATION: { // 0x00000100, NV2A_NOP, No Operation, followed parameters are no use. this operation triggers DPC which is not implemented in HLE
|
||||
break;
|
||||
}
|
||||
case NV097_SET_DEPTH_FUNC: { // 0x00000354
|
||||
// Test-case : Whiplash
|
||||
SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZFUNC,
|
||||
parameter & 0xF);
|
||||
break;
|
||||
}
|
||||
case NV097_SET_DEPTH_TEST_ENABLE: { // 0x0000030C, NV2A_DEPTH_TEST_ENABLE
|
||||
// Test-case : Whiplash
|
||||
SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZENABLE,
|
||||
parameter);
|
||||
break;
|
||||
}
|
||||
case NV097_SET_TRANSFORM_CONSTANT: // 0x00000B80, NV2A_VP_UPLOAD_CONST(0), D3DPUSH_SET_TRANSFORM_CONSTANT
|
||||
case NV2A_VP_UPLOAD_CONST(1):
|
||||
case NV2A_VP_UPLOAD_CONST(2):
|
||||
case NV2A_VP_UPLOAD_CONST(3): {
|
||||
// Can't use NOINCREMENT_FLAG, parameters is constant matrix, 4X4 matrix has 16 DWORDs, maximum of 32 DWORD writes
|
||||
//load constant matrix to empty slot
|
||||
LOG_TEST_CASE("NV2A_VP_UPLOAD_CONST");
|
||||
break;
|
||||
}
|
||||
case NV097_SET_BEGIN_END: { // 0x000017FC, NV2A_VERTEX_BEGIN_END, D3DPUSH_SET_BEGIN_END, 1 DWORD parameter
|
||||
if (parameter == 0) { // Parameter == 0 means SetEnd, EndPush()
|
||||
// Trigger all draws from here
|
||||
if (pg->draw_arrays_length) {
|
||||
LOG_TEST_CASE("PushBuffer : Draw Arrays");
|
||||
assert(pg->inline_buffer_length == 0);
|
||||
assert(pg->inline_array_length == 0);
|
||||
assert(pg->inline_elements_length == 0);
|
||||
|
||||
#if 0 // LLE OpenGL
|
||||
pgraph_bind_vertex_attributes(d, pg->draw_arrays_max_count,
|
||||
false, 0);
|
||||
glMultiDrawArrays(pg->shader_binding->gl_primitive_mode,
|
||||
pg->gl_draw_arrays_start,
|
||||
pg->gl_draw_arrays_count,
|
||||
pg->draw_arrays_length);
|
||||
#else
|
||||
// TODO : Implement this
|
||||
#endif
|
||||
}
|
||||
else if (pg->inline_buffer_length) {
|
||||
LOG_TEST_CASE("PushBuffer : Inline Buffer");
|
||||
assert(pg->draw_arrays_length == 0);
|
||||
assert(pg->inline_array_length == 0);
|
||||
assert(pg->inline_elements_length == 0);
|
||||
|
||||
#if 0 // LLE OpenGL
|
||||
for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
||||
VertexAttribute *vertex_attribute = &pg->vertex_attributes[i];
|
||||
if (vertex_attribute->inline_buffer) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER,
|
||||
vertex_attribute->gl_inline_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
pg->inline_buffer_length
|
||||
* sizeof(float) * 4,
|
||||
vertex_attribute->inline_buffer,
|
||||
GL_DYNAMIC_DRAW);
|
||||
/* Clear buffer for next batch */
|
||||
g_free(vertex_attribute->inline_buffer);
|
||||
vertex_attribute->inline_buffer = NULL;
|
||||
glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glEnableVertexAttribArray(i);
|
||||
}
|
||||
else {
|
||||
glDisableVertexAttribArray(i);
|
||||
glVertexAttrib4fv(i, vertex_attribute->inline_value);
|
||||
}
|
||||
}
|
||||
|
||||
glDrawArrays(pg->shader_binding->gl_primitive_mode,
|
||||
0, pg->inline_buffer_length);
|
||||
#else
|
||||
// TODO : Implement this
|
||||
#endif
|
||||
}
|
||||
else if (pg->inline_array_length) {
|
||||
// NV2A_GL_DPRINTF(false, "Inline Array");
|
||||
assert(pg->draw_arrays_length == 0);
|
||||
assert(pg->inline_buffer_length == 0);
|
||||
assert(pg->inline_elements_length == 0);
|
||||
|
||||
#if 0 // LLE OpenGL
|
||||
unsigned int index_count = pgraph_bind_inline_array(d);
|
||||
glDrawArrays(pg->shader_binding->gl_primitive_mode, 0, index_count);
|
||||
#else
|
||||
//DWORD vertex data array,
|
||||
//To be used as a replacement for DrawVerticesUP, the caller needs to set the vertex format using IDirect3DDevice8::SetVertexShader before calling BeginPush. All attributes in the vertex format must be padded DWORD multiples, and the vertex attributes must be specified in the canonical FVF ordering (position followed by weight, normal, diffuse, and so on).
|
||||
// retrieve vertex shader
|
||||
XTL::DWORD dwVertexShader = g_CurrentXboxVertexShaderHandle;
|
||||
if (dwVertexShader == 0) {
|
||||
LOG_TEST_CASE("FVF Vertex Shader is null");
|
||||
dwVertexShader = -1;
|
||||
}
|
||||
|
||||
// render vertices
|
||||
if (dwVertexShader != -1) {
|
||||
XTL::DWORD dwVertexStride = CxbxGetStrideFromVertexShaderHandle(dwVertexShader);
|
||||
if (dwVertexStride > 0) {
|
||||
XTL::UINT VertexCount = (pg->inline_array_length * sizeof(XTL::DWORD)) / dwVertexStride;
|
||||
CxbxDrawContext DrawContext = {};
|
||||
|
||||
DrawContext.XboxPrimitiveType = (X_D3DPRIMITIVETYPE)pg->primitive_mode;
|
||||
DrawContext.dwVertexCount = VertexCount;
|
||||
DrawContext.pXboxVertexStreamZeroData = pg->inline_array;
|
||||
DrawContext.uiXboxVertexStreamZeroStride = dwVertexStride;
|
||||
DrawContext.hVertexShader = dwVertexShader;
|
||||
|
||||
CxbxDrawPrimitiveUP(DrawContext);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (pg->inline_elements_length) {
|
||||
// NV2A_GL_DPRINTF(false, "Inline Elements");
|
||||
assert(pg->draw_arrays_length == 0);
|
||||
assert(pg->inline_buffer_length == 0);
|
||||
assert(pg->inline_array_length == 0);
|
||||
|
||||
#if 0 // LLE OpenGL
|
||||
uint32_t max_element = 0;
|
||||
uint32_t min_element = (uint32_t)-1;
|
||||
for (i = 0; i<pg->inline_elements_length; i++) {
|
||||
max_element = MAX(pg->inline_elements[i], max_element);
|
||||
min_element = MIN(pg->inline_elements[i], min_element);
|
||||
}
|
||||
|
||||
pgraph_bind_vertex_attributes(d, max_element + 1, false, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pg->gl_element_buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||
pg->inline_elements_length * 4,
|
||||
pg->inline_elements,
|
||||
GL_DYNAMIC_DRAW);
|
||||
glDrawRangeElements(pg->shader_binding->gl_primitive_mode,
|
||||
min_element, max_element,
|
||||
pg->inline_elements_length,
|
||||
GL_UNSIGNED_INT,
|
||||
(void*)0);
|
||||
#else
|
||||
if (IsValidCurrentShader()) {
|
||||
unsigned int uiIndexCount = pg->inline_elements_length;
|
||||
CxbxDrawContext DrawContext = {};
|
||||
|
||||
DrawContext.XboxPrimitiveType = (X_D3DPRIMITIVETYPE)pg->primitive_mode;
|
||||
DrawContext.dwVertexCount = EmuD3DIndexCountToVertexCount(DrawContext.XboxPrimitiveType, uiIndexCount);
|
||||
DrawContext.hVertexShader = g_CurrentXboxVertexShaderHandle;
|
||||
DrawContext.pIndexData = pg__inline_elements; // Used by GetVerticesInBuffer
|
||||
|
||||
CxbxDrawIndexed(DrawContext);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
LOG_TEST_CASE("EMPTY NV097_SET_BEGIN_END");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// NV2A_GL_DGROUP_BEGIN("NV097_SET_BEGIN_END: 0x%x", parameter);
|
||||
assert(parameter <= NV097_SET_BEGIN_END_OP_POLYGON);
|
||||
|
||||
pg->primitive_mode = parameter; // Retrieve the D3DPRIMITIVETYPE info in parameter
|
||||
|
||||
CxbxUpdateNativeD3DResources();
|
||||
|
||||
pg->inline_elements_length = 0;
|
||||
pg->inline_array_length = 0;
|
||||
pg->inline_buffer_length = 0;
|
||||
pg->draw_arrays_length = 0;
|
||||
pg->draw_arrays_max_count = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case NV097_ARRAY_ELEMENT16: { // 0x1800, NV2A_VB_ELEMENT_U16
|
||||
//LOG_TEST_CASE("NV2A_VB_ELEMENT_U16");
|
||||
// Test-case : Turok (in main menu)
|
||||
// Test-case : Hunter Redeemer
|
||||
// Test-case : Otogi (see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/pull/1113#issuecomment-385593814)
|
||||
assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH);
|
||||
pg__inline_elements[
|
||||
pg->inline_elements_length++] = parameter & 0xFFFF;
|
||||
pg__inline_elements[
|
||||
pg->inline_elements_length++] = parameter >> 16;
|
||||
break;
|
||||
}
|
||||
case NV097_ARRAY_ELEMENT32: { // 0x1808, NV2A_VB_ELEMENT_U32, Index Array Data
|
||||
//LOG_TEST_CASE("NV2A_VB_ELEMENT_U32");
|
||||
// Test-case : Turok (in main menu)
|
||||
assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH);
|
||||
pg__inline_elements[
|
||||
pg->inline_elements_length++] = parameter;
|
||||
break;
|
||||
}
|
||||
case NV097_INLINE_ARRAY: { // 0x1818, NV2A_VERTEX_DATA, parameter size= dwCount*DWORD, represent D3DFVF data
|
||||
assert(pg->inline_array_length < NV2A_MAX_BATCH_LENGTH);
|
||||
pg->inline_array[
|
||||
pg->inline_array_length++] = parameter;
|
||||
break;
|
||||
}
|
||||
case NV097_SET_TRANSFORM_EXECUTION_MODE: { // 0x00001E94, NV2A_ENGINE
|
||||
// Test-case : Whiplash
|
||||
SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_MODE,
|
||||
GET_MASK(parameter,
|
||||
NV097_SET_TRANSFORM_EXECUTION_MODE_MODE));
|
||||
SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_RANGE_MODE,
|
||||
GET_MASK(parameter,
|
||||
NV097_SET_TRANSFORM_EXECUTION_MODE_RANGE_MODE));
|
||||
break;
|
||||
}
|
||||
case NV097_SET_TRANSFORM_PROGRAM_CXT_WRITE_EN: { // 0x00001E98, NV2A_SET_TRANSFORM_PROGRAM_CXT_WRITE_EN
|
||||
// Test-case : Whiplash
|
||||
pg->enable_vertex_program_write = parameter;
|
||||
break;
|
||||
}
|
||||
case NV097_SET_TRANSFORM_CONSTANT_LOAD: { // 0x00001EA4, NV2A_VP_UPLOAD_CONST_ID, D3DPUSH_SET_TRANSFORM_CONSTANT_LOAD
|
||||
// Add 96 to constant index parameter, one parameter=CONSTANT + 96
|
||||
// Retrieve transform constant index and add 96 to it.
|
||||
LOG_TEST_CASE("NV2A_VP_UPLOAD_CONST_ID");
|
||||
break;
|
||||
}
|
||||
default: { // default case, handling any other unknown methods.
|
||||
char message[256] = {};
|
||||
sprintf(message, "Unhandled PushBuffer Operation : %s (0x%.04X)", NV2AMethodToString(method), method);
|
||||
LOG_TEST_CASE(message);
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
LOG_UNIMPLEMENTED(); // TODO : Implement HLE_draw_arrays
|
||||
}
|
||||
|
||||
void HLE_draw_inline_buffer(NV2AState *d)
|
||||
{
|
||||
// PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
using namespace XTL;
|
||||
|
||||
LOG_TEST_CASE("HLE_draw_inline_buffer");
|
||||
|
||||
LOG_UNIMPLEMENTED(); // TODO : Implement HLE_draw_inline_buffer
|
||||
}
|
||||
|
||||
void HLE_draw_inline_array(NV2AState *d)
|
||||
{
|
||||
PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
using namespace XTL;
|
||||
|
||||
CxbxUpdateNativeD3DResources();
|
||||
|
||||
//DWORD vertex data array,
|
||||
//To be used as a replacement for DrawVerticesUP, the caller needs to set the vertex format using IDirect3DDevice8::SetVertexShader before calling BeginPush. All attributes in the vertex format must be padded DWORD multiples, and the vertex attributes must be specified in the canonical FVF ordering (position followed by weight, normal, diffuse, and so on).
|
||||
// retrieve vertex shader
|
||||
XTL::DWORD dwVertexShader = g_CurrentXboxVertexShaderHandle;
|
||||
if (dwVertexShader == 0) {
|
||||
LOG_TEST_CASE("FVF Vertex Shader is null");
|
||||
dwVertexShader = -1;
|
||||
}
|
||||
|
||||
// render vertices
|
||||
if (dwVertexShader != -1) {
|
||||
XTL::DWORD dwVertexStride = CxbxGetStrideFromVertexShaderHandle(dwVertexShader);
|
||||
if (dwVertexStride > 0) {
|
||||
XTL::UINT VertexCount = (pg->inline_array_length * sizeof(XTL::DWORD)) / dwVertexStride;
|
||||
CxbxDrawContext DrawContext = {};
|
||||
|
||||
DrawContext.XboxPrimitiveType = (X_D3DPRIMITIVETYPE)pg->primitive_mode;
|
||||
DrawContext.dwVertexCount = VertexCount;
|
||||
DrawContext.pXboxVertexStreamZeroData = pg->inline_array;
|
||||
DrawContext.uiXboxVertexStreamZeroStride = dwVertexStride;
|
||||
DrawContext.hVertexShader = dwVertexShader;
|
||||
|
||||
CxbxDrawPrimitiveUP(DrawContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HLE_draw_inline_elements(NV2AState *d)
|
||||
{
|
||||
PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
using namespace XTL;
|
||||
|
||||
if (IsValidCurrentShader()) {
|
||||
unsigned int uiIndexCount = pg->inline_elements_length;
|
||||
CxbxDrawContext DrawContext = {};
|
||||
|
||||
DrawContext.XboxPrimitiveType = (X_D3DPRIMITIVETYPE)pg->primitive_mode;
|
||||
DrawContext.dwVertexCount = EmuD3DIndexCountToVertexCount(DrawContext.XboxPrimitiveType, uiIndexCount);
|
||||
DrawContext.hVertexShader = g_CurrentXboxVertexShaderHandle;
|
||||
DrawContext.pIndexData = d->pgraph.inline_elements; // Used by GetVerticesInBuffer
|
||||
|
||||
CxbxDrawIndexed(DrawContext);
|
||||
}
|
||||
}
|
||||
|
||||
void HLE_draw_state_update(NV2AState *d)
|
||||
{
|
||||
// PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
using namespace XTL;
|
||||
|
||||
CxbxUpdateNativeD3DResources();
|
||||
|
||||
LOG_INCOMPLETE(); // TODO : Read state from pgraph, convert to D3D
|
||||
}
|
||||
|
||||
// Import pgraph_draw_* variables, declared in EmuNV2A_PGRAPH.cpp :
|
||||
extern void(*pgraph_draw_arrays)(NV2AState *d);
|
||||
extern void(*pgraph_draw_inline_buffer)(NV2AState *d);
|
||||
extern void(*pgraph_draw_inline_array)(NV2AState *d);
|
||||
extern void(*pgraph_draw_inline_elements)(NV2AState *d);
|
||||
extern void(*pgraph_draw_state_update)(NV2AState *d);
|
||||
|
||||
void HLE_init_pgraph_plugins()
|
||||
{
|
||||
/* attach HLE Direct3D render plugins */
|
||||
pgraph_draw_arrays = HLE_draw_arrays;
|
||||
pgraph_draw_inline_buffer = HLE_draw_inline_buffer;
|
||||
pgraph_draw_inline_array = HLE_draw_inline_array;
|
||||
pgraph_draw_inline_elements = HLE_draw_inline_elements;
|
||||
pgraph_draw_state_update = HLE_draw_state_update;
|
||||
}
|
||||
|
||||
extern void pgraph_handle_method(
|
||||
NV2AState *d,
|
||||
unsigned int subchannel,
|
||||
unsigned int method,
|
||||
uint32_t parameter);
|
||||
|
||||
// LLE NV2A
|
||||
extern NV2ADevice* g_NV2A;
|
||||
|
||||
void HLE_write_NV2A_vertex_attribute_slot(unsigned slot, uint32_t parameter)
|
||||
{
|
||||
// Write value to HLE version of the NV2A
|
||||
#if 0 // TODO : Enable this once HLE_pgraph_handle_method supports NV097_SET_VERTEX_DATA4UB :
|
||||
HLE_pgraph_handle_method(&pgraph_state,
|
||||
// Write value to LLE NV2A device
|
||||
pgraph_handle_method(g_NV2A->GetDeviceState(),
|
||||
/*subchannel=*/0,
|
||||
/*method=*/NV097_SET_VERTEX_DATA4UB + (4 * slot),
|
||||
parameter);
|
||||
#else
|
||||
// EmuNV2A_PGRAPH immitation
|
||||
PGRAPHState *pg = &pgraph_state;
|
||||
|
||||
VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot];
|
||||
//pgraph_allocate_inline_buffer_vertices(pg, slot); // Present in LLE pgraph_handle_method() but not needed here
|
||||
vertex_attribute->inline_value[0] = (parameter & 0xFF) / 255.0f;
|
||||
vertex_attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0f;
|
||||
vertex_attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0f;
|
||||
vertex_attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t HLE_read_NV2A_vertex_attribute_slot(unsigned slot)
|
||||
{
|
||||
// EmuNV2A_PGRAPH immitation
|
||||
PGRAPHState *pg = &pgraph_state;
|
||||
NV2AState* dev = g_NV2A->GetDeviceState();
|
||||
PGRAPHState *pg = &(dev->pgraph);
|
||||
|
||||
// See CASE_16(NV097_SET_VERTEX_DATA4UB, 4) in LLE pgraph_handle_method()
|
||||
VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot];
|
||||
// Inverse of D3DDevice_SetVertexDataColor
|
||||
|
@ -514,9 +353,15 @@ uint32_t HLE_read_NV2A_vertex_attribute_slot(unsigned slot)
|
|||
|
||||
// For now, skip the cache, but handle the pgraph method directly
|
||||
// Note : Here's where the method gets multiplied by four!
|
||||
// Note 2 : pg is read from local scope, and ni is unused (same in LLE)
|
||||
// Note 2 : d is read from local scope, and ni is unused (same in LLE)
|
||||
// Note 3 : Keep EmuExecutePushBufferRaw skipping all commands not intended for channel 0 (3D)
|
||||
// Note 4 : Prevent a crash during shutdown when g_NV2A gets deleted
|
||||
#define CACHE_PUSH(subc, mthd, word, ni) \
|
||||
HLE_pgraph_handle_method(pg, subc, mthd << 2, word)
|
||||
if (subc == 0) { \
|
||||
if (g_NV2A) { \
|
||||
pgraph_handle_method(d, subc, mthd << 2, word); \
|
||||
} \
|
||||
}
|
||||
|
||||
typedef union {
|
||||
/* https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#the-commands-pre-gf100-format
|
||||
|
@ -562,6 +407,8 @@ extern void XTL::EmuExecutePushBufferRaw
|
|||
uint32_t uSizeInBytes
|
||||
)
|
||||
{
|
||||
HLE_init_pgraph_plugins(); // TODO : Move to more approriate spot
|
||||
|
||||
// Test-case : Azurik (see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/360)
|
||||
// Test-case : Crash 'n' Burn [45530014]
|
||||
// Test-case : CrimsonSea [4B4F0002]
|
||||
|
@ -582,8 +429,9 @@ extern void XTL::EmuExecutePushBufferRaw
|
|||
assert(pPushData);
|
||||
assert(uSizeInBytes >= 4);
|
||||
|
||||
// EmuNV2A_PGRAPH immitation
|
||||
PGRAPHState *pg = &pgraph_state;
|
||||
// Retrieve NV2AState via the (LLE) NV2A device :
|
||||
NV2AState *d = g_NV2A->GetDeviceState();
|
||||
d->pgraph.channel_valid = true; // avoid assert
|
||||
|
||||
// DMA Pusher state -- see https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#pusher-state
|
||||
#if 0
|
||||
|
|
|
@ -43,9 +43,6 @@
|
|||
#include "CxbxKrnl/EmuXTL.h"
|
||||
#include "CxbxKrnl/ResourceTracker.h"
|
||||
|
||||
// TODO: Find somewhere to put this that doesn't conflict with XTL::
|
||||
extern void EmuUpdateActiveTextureStages();
|
||||
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
|
|
|
@ -401,10 +401,16 @@ static const SurfaceColorFormatInfo kelvin_surface_color_format_map[16] = {
|
|||
{}
|
||||
};
|
||||
|
||||
void (*pgraph_draw_arrays)(NV2AState *d);
|
||||
void (*pgraph_draw_inline_buffer)(NV2AState *d);
|
||||
void (*pgraph_draw_inline_array)(NV2AState *d);
|
||||
void (*pgraph_draw_inline_elements)(NV2AState *d);
|
||||
void (*pgraph_draw_state_update)(NV2AState *d);
|
||||
|
||||
static void pgraph_switch_context(NV2AState *d, unsigned int channel_id);
|
||||
static void pgraph_set_context_user(NV2AState *d, uint32_t value);
|
||||
static void pgraph_wait_fifo_access(NV2AState *d);
|
||||
static void pgraph_handle_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter);
|
||||
void pgraph_handle_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter);
|
||||
static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, unsigned int attr);
|
||||
static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg);
|
||||
static void pgraph_update_shader_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function);
|
||||
|
@ -576,7 +582,292 @@ DEVICE_WRITE32(PGRAPH)
|
|||
DEVICE_WRITE32_END(PGRAPH);
|
||||
}
|
||||
|
||||
static void pgraph_handle_method(NV2AState *d,
|
||||
void OpenGL_draw_arrays(NV2AState *d)
|
||||
{
|
||||
PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
assert(pg->opengl_enabled);
|
||||
|
||||
pgraph_bind_vertex_attributes(d, pg->draw_arrays_max_count,
|
||||
false, 0);
|
||||
glMultiDrawArrays(pg->shader_binding->gl_primitive_mode,
|
||||
pg->gl_draw_arrays_start,
|
||||
pg->gl_draw_arrays_count,
|
||||
pg->draw_arrays_length);
|
||||
}
|
||||
|
||||
void OpenGL_draw_inline_buffer(NV2AState *d)
|
||||
{
|
||||
PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
assert(pg->opengl_enabled);
|
||||
|
||||
for (unsigned int i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
||||
VertexAttribute *vertex_attribute = &pg->vertex_attributes[i];
|
||||
|
||||
if (vertex_attribute->inline_buffer) {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER,
|
||||
vertex_attribute->gl_inline_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
pg->inline_buffer_length
|
||||
* sizeof(float) * 4,
|
||||
vertex_attribute->inline_buffer,
|
||||
GL_DYNAMIC_DRAW);
|
||||
|
||||
/* Clear buffer for next batch */
|
||||
g_free(vertex_attribute->inline_buffer);
|
||||
vertex_attribute->inline_buffer = NULL;
|
||||
|
||||
glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glEnableVertexAttribArray(i);
|
||||
}
|
||||
else {
|
||||
glDisableVertexAttribArray(i);
|
||||
glVertexAttrib4fv(i, vertex_attribute->inline_value);
|
||||
}
|
||||
}
|
||||
|
||||
glDrawArrays(pg->shader_binding->gl_primitive_mode,
|
||||
0, pg->inline_buffer_length);
|
||||
}
|
||||
|
||||
void OpenGL_draw_inline_array(NV2AState *d)
|
||||
{
|
||||
PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
assert(pg->opengl_enabled);
|
||||
|
||||
unsigned int index_count = pgraph_bind_inline_array(d);
|
||||
glDrawArrays(pg->shader_binding->gl_primitive_mode,
|
||||
0, index_count);
|
||||
}
|
||||
|
||||
void OpenGL_draw_inline_elements(NV2AState *d)
|
||||
{
|
||||
PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
assert(pg->opengl_enabled);
|
||||
|
||||
uint32_t max_element = 0;
|
||||
uint32_t min_element = (uint32_t)-1;
|
||||
for (unsigned int i = 0; i < pg->inline_elements_length; i++) {
|
||||
max_element = MAX(pg->inline_elements[i], max_element);
|
||||
min_element = MIN(pg->inline_elements[i], min_element);
|
||||
}
|
||||
pgraph_bind_vertex_attributes(d, max_element + 1, false, 0);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pg->gl_element_buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||
pg->inline_elements_length * sizeof(pg->inline_elements[0]),
|
||||
pg->inline_elements,
|
||||
GL_DYNAMIC_DRAW);
|
||||
|
||||
glDrawRangeElements(pg->shader_binding->gl_primitive_mode,
|
||||
min_element, max_element,
|
||||
pg->inline_elements_length,
|
||||
GL_UNSIGNED_SHORT, // Cxbx-Reloaded TODO : Restore GL_UNSIGNED_INT once HLE_draw_inline_elements can draw using uint32_t
|
||||
(void*)0);
|
||||
}
|
||||
|
||||
void OpenGL_draw_state_update(NV2AState *d)
|
||||
{
|
||||
PGRAPHState *pg = &d->pgraph;
|
||||
|
||||
assert(pg->opengl_enabled);
|
||||
|
||||
uint32_t control_0 = pg->regs[NV_PGRAPH_CONTROL_0];
|
||||
uint32_t control_1 = pg->regs[NV_PGRAPH_CONTROL_1];
|
||||
|
||||
bool depth_test = control_0
|
||||
& NV_PGRAPH_CONTROL_0_ZENABLE;
|
||||
bool stencil_test = control_1
|
||||
& NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE;
|
||||
|
||||
pgraph_update_surface(d, true, true, depth_test || stencil_test);
|
||||
|
||||
bool alpha = control_0 & NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE;
|
||||
bool red = control_0 & NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE;
|
||||
bool green = control_0 & NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE;
|
||||
bool blue = control_0 & NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE;
|
||||
glColorMask(red, green, blue, alpha);
|
||||
glDepthMask(!!(control_0 & NV_PGRAPH_CONTROL_0_ZWRITEENABLE));
|
||||
glStencilMask(GET_MASK(control_1,
|
||||
NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE));
|
||||
|
||||
uint32_t blend = pg->regs[NV_PGRAPH_BLEND];
|
||||
if (blend & NV_PGRAPH_BLEND_EN) {
|
||||
glEnable(GL_BLEND);
|
||||
uint32_t sfactor = GET_MASK(blend,
|
||||
NV_PGRAPH_BLEND_SFACTOR);
|
||||
uint32_t dfactor = GET_MASK(blend,
|
||||
NV_PGRAPH_BLEND_DFACTOR);
|
||||
assert(sfactor < ARRAY_SIZE(pgraph_blend_factor_map));
|
||||
assert(dfactor < ARRAY_SIZE(pgraph_blend_factor_map));
|
||||
glBlendFunc(pgraph_blend_factor_map[sfactor],
|
||||
pgraph_blend_factor_map[dfactor]);
|
||||
|
||||
uint32_t equation = GET_MASK(blend,
|
||||
NV_PGRAPH_BLEND_EQN);
|
||||
assert(equation < ARRAY_SIZE(pgraph_blend_equation_map));
|
||||
glBlendEquation(pgraph_blend_equation_map[equation]);
|
||||
|
||||
uint32_t blend_color = pg->regs[NV_PGRAPH_BLENDCOLOR];
|
||||
glBlendColor(((blend_color >> 16) & 0xFF) / 255.0f, /* red */
|
||||
((blend_color >> 8) & 0xFF) / 255.0f, /* green */
|
||||
(blend_color & 0xFF) / 255.0f, /* blue */
|
||||
((blend_color >> 24) & 0xFF) / 255.0f);/* alpha */
|
||||
}
|
||||
else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
/* Face culling */
|
||||
uint32_t setupraster = pg->regs[NV_PGRAPH_SETUPRASTER];
|
||||
if (setupraster
|
||||
& NV_PGRAPH_SETUPRASTER_CULLENABLE) {
|
||||
uint32_t cull_face = GET_MASK(setupraster,
|
||||
NV_PGRAPH_SETUPRASTER_CULLCTRL);
|
||||
assert(cull_face < ARRAY_SIZE(pgraph_cull_face_map));
|
||||
glCullFace(pgraph_cull_face_map[cull_face]);
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
/* Front-face select */
|
||||
glFrontFace(setupraster
|
||||
& NV_PGRAPH_SETUPRASTER_FRONTFACE
|
||||
? GL_CCW : GL_CW);
|
||||
|
||||
/* Polygon offset */
|
||||
/* FIXME: GL implementation-specific, maybe do this in VS? */
|
||||
if (setupraster &
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE) {
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
}
|
||||
if (setupraster &
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE) {
|
||||
glEnable(GL_POLYGON_OFFSET_LINE);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_POLYGON_OFFSET_LINE);
|
||||
}
|
||||
if (setupraster &
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE) {
|
||||
glEnable(GL_POLYGON_OFFSET_POINT);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_POLYGON_OFFSET_POINT);
|
||||
}
|
||||
if (setupraster &
|
||||
(NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE |
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE |
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) {
|
||||
GLfloat zfactor = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETFACTOR];
|
||||
GLfloat zbias = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETBIAS];
|
||||
glPolygonOffset(zfactor, zbias);
|
||||
}
|
||||
|
||||
/* Depth testing */
|
||||
if (depth_test) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
uint32_t depth_func = GET_MASK(control_0,
|
||||
NV_PGRAPH_CONTROL_0_ZFUNC);
|
||||
assert(depth_func < ARRAY_SIZE(pgraph_depth_func_map));
|
||||
glDepthFunc(pgraph_depth_func_map[depth_func]);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
if (stencil_test) {
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
uint32_t stencil_func = GET_MASK(control_1,
|
||||
NV_PGRAPH_CONTROL_1_STENCIL_FUNC);
|
||||
uint32_t stencil_ref = GET_MASK(control_1,
|
||||
NV_PGRAPH_CONTROL_1_STENCIL_REF);
|
||||
uint32_t func_mask = GET_MASK(control_1,
|
||||
NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ);
|
||||
uint32_t control2 = pg->regs[NV_PGRAPH_CONTROL_2];
|
||||
uint32_t op_fail = GET_MASK(control2,
|
||||
NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL);
|
||||
uint32_t op_zfail = GET_MASK(control2,
|
||||
NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL);
|
||||
uint32_t op_zpass = GET_MASK(control2,
|
||||
NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS);
|
||||
|
||||
assert(stencil_func < ARRAY_SIZE(pgraph_stencil_func_map));
|
||||
assert(op_fail < ARRAY_SIZE(pgraph_stencil_op_map));
|
||||
assert(op_zfail < ARRAY_SIZE(pgraph_stencil_op_map));
|
||||
assert(op_zpass < ARRAY_SIZE(pgraph_stencil_op_map));
|
||||
|
||||
glStencilFunc(
|
||||
pgraph_stencil_func_map[stencil_func],
|
||||
stencil_ref,
|
||||
func_mask);
|
||||
|
||||
glStencilOp(
|
||||
pgraph_stencil_op_map[op_fail],
|
||||
pgraph_stencil_op_map[op_zfail],
|
||||
pgraph_stencil_op_map[op_zpass]);
|
||||
|
||||
}
|
||||
else {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
/* Dither */
|
||||
/* FIXME: GL implementation dependent */
|
||||
if (control_0 &
|
||||
NV_PGRAPH_CONTROL_0_DITHERENABLE) {
|
||||
glEnable(GL_DITHER);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_DITHER);
|
||||
}
|
||||
|
||||
pgraph_bind_shaders(pg);
|
||||
pgraph_bind_textures(d);
|
||||
|
||||
//glDisableVertexAttribArray(NV2A_VERTEX_ATTR_DIFFUSE);
|
||||
//glVertexAttrib4f(NV2A_VERTEX_ATTR_DIFFUSE, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
unsigned int width, height;
|
||||
pgraph_get_surface_dimensions(pg, &width, &height);
|
||||
pgraph_apply_anti_aliasing_factor(pg, &width, &height);
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
/* Visibility testing */
|
||||
if (pg->zpass_pixel_count_enable) {
|
||||
GLuint gl_query;
|
||||
glGenQueries(1, &gl_query);
|
||||
pg->gl_zpass_pixel_count_query_count++;
|
||||
pg->gl_zpass_pixel_count_queries = (GLuint*)g_realloc(
|
||||
pg->gl_zpass_pixel_count_queries,
|
||||
sizeof(GLuint) * pg->gl_zpass_pixel_count_query_count);
|
||||
pg->gl_zpass_pixel_count_queries[
|
||||
pg->gl_zpass_pixel_count_query_count - 1] = gl_query;
|
||||
glBeginQuery(GL_SAMPLES_PASSED, gl_query);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL_init_pgraph_plugins()
|
||||
{
|
||||
pgraph_draw_arrays = OpenGL_draw_arrays;
|
||||
pgraph_draw_inline_buffer = OpenGL_draw_inline_buffer;
|
||||
pgraph_draw_inline_array = OpenGL_draw_inline_array;
|
||||
pgraph_draw_inline_elements = OpenGL_draw_inline_elements;
|
||||
pgraph_draw_state_update = OpenGL_draw_state_update;
|
||||
}
|
||||
|
||||
void pgraph_handle_method(NV2AState *d,
|
||||
unsigned int subchannel,
|
||||
unsigned int method,
|
||||
uint32_t parameter)
|
||||
|
@ -1841,294 +2132,80 @@ static void pgraph_handle_method(NV2AState *d,
|
|||
bool depth_test = control_0
|
||||
& NV_PGRAPH_CONTROL_0_ZENABLE;
|
||||
bool stencil_test = control_1
|
||||
& NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE;
|
||||
& NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE;
|
||||
|
||||
if (pg->opengl_enabled) {
|
||||
if (parameter == NV097_SET_BEGIN_END_OP_END) {
|
||||
if (parameter == NV097_SET_BEGIN_END_OP_END) {
|
||||
|
||||
if (pg->opengl_enabled) {
|
||||
assert(pg->shader_binding);
|
||||
}
|
||||
|
||||
if (pg->draw_arrays_length) {
|
||||
if (pg->draw_arrays_length) {
|
||||
|
||||
NV2A_GL_DPRINTF(false, "Draw Arrays");
|
||||
NV2A_GL_DPRINTF(false, "Draw Arrays");
|
||||
|
||||
assert(pg->inline_buffer_length == 0);
|
||||
assert(pg->inline_array_length == 0);
|
||||
assert(pg->inline_elements_length == 0);
|
||||
assert(pg->inline_buffer_length == 0);
|
||||
assert(pg->inline_array_length == 0);
|
||||
assert(pg->inline_elements_length == 0);
|
||||
|
||||
pgraph_bind_vertex_attributes(d, pg->draw_arrays_max_count,
|
||||
false, 0);
|
||||
glMultiDrawArrays(pg->shader_binding->gl_primitive_mode,
|
||||
pg->gl_draw_arrays_start,
|
||||
pg->gl_draw_arrays_count,
|
||||
pg->draw_arrays_length);
|
||||
} else if (pg->inline_buffer_length) {
|
||||
pgraph_draw_arrays(d);
|
||||
} else if (pg->inline_buffer_length) {
|
||||
|
||||
NV2A_GL_DPRINTF(false, "Inline Buffer");
|
||||
NV2A_GL_DPRINTF(false, "Inline Buffer");
|
||||
|
||||
assert(pg->draw_arrays_length == 0);
|
||||
assert(pg->inline_array_length == 0);
|
||||
assert(pg->inline_elements_length == 0);
|
||||
assert(pg->draw_arrays_length == 0);
|
||||
assert(pg->inline_array_length == 0);
|
||||
assert(pg->inline_elements_length == 0);
|
||||
|
||||
for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
||||
VertexAttribute *vertex_attribute = &pg->vertex_attributes[i];
|
||||
pgraph_draw_inline_buffer(d);
|
||||
} else if (pg->inline_array_length) {
|
||||
|
||||
if (vertex_attribute->inline_buffer) {
|
||||
NV2A_GL_DPRINTF(false, "Inline Array");
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER,
|
||||
vertex_attribute->gl_inline_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
pg->inline_buffer_length
|
||||
* sizeof(float) * 4,
|
||||
vertex_attribute->inline_buffer,
|
||||
GL_DYNAMIC_DRAW);
|
||||
assert(pg->draw_arrays_length == 0);
|
||||
assert(pg->inline_buffer_length == 0);
|
||||
assert(pg->inline_elements_length == 0);
|
||||
|
||||
/* Clear buffer for next batch */
|
||||
g_free(vertex_attribute->inline_buffer);
|
||||
vertex_attribute->inline_buffer = NULL;
|
||||
pgraph_draw_inline_array(d);
|
||||
} else if (pg->inline_elements_length) {
|
||||
|
||||
glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glEnableVertexAttribArray(i);
|
||||
} else {
|
||||
glDisableVertexAttribArray(i);
|
||||
NV2A_GL_DPRINTF(false, "Inline Elements");
|
||||
|
||||
glVertexAttrib4fv(i, vertex_attribute->inline_value);
|
||||
}
|
||||
assert(pg->draw_arrays_length == 0);
|
||||
assert(pg->inline_buffer_length == 0);
|
||||
assert(pg->inline_array_length == 0);
|
||||
|
||||
}
|
||||
|
||||
glDrawArrays(pg->shader_binding->gl_primitive_mode,
|
||||
0, pg->inline_buffer_length);
|
||||
} else if (pg->inline_array_length) {
|
||||
|
||||
NV2A_GL_DPRINTF(false, "Inline Array");
|
||||
|
||||
assert(pg->draw_arrays_length == 0);
|
||||
assert(pg->inline_buffer_length == 0);
|
||||
assert(pg->inline_elements_length == 0);
|
||||
|
||||
unsigned int index_count = pgraph_bind_inline_array(d);
|
||||
glDrawArrays(pg->shader_binding->gl_primitive_mode,
|
||||
0, index_count);
|
||||
} else if (pg->inline_elements_length) {
|
||||
|
||||
NV2A_GL_DPRINTF(false, "Inline Elements");
|
||||
|
||||
assert(pg->draw_arrays_length == 0);
|
||||
assert(pg->inline_buffer_length == 0);
|
||||
assert(pg->inline_array_length == 0);
|
||||
|
||||
uint32_t max_element = 0;
|
||||
uint32_t min_element = (uint32_t)-1;
|
||||
for (i=0; i<pg->inline_elements_length; i++) {
|
||||
max_element = MAX(pg->inline_elements[i], max_element);
|
||||
min_element = MIN(pg->inline_elements[i], min_element);
|
||||
}
|
||||
|
||||
pgraph_bind_vertex_attributes(d, max_element+1, false, 0);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pg->gl_element_buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||
pg->inline_elements_length*4,
|
||||
pg->inline_elements,
|
||||
GL_DYNAMIC_DRAW);
|
||||
|
||||
glDrawRangeElements(pg->shader_binding->gl_primitive_mode,
|
||||
min_element, max_element,
|
||||
pg->inline_elements_length,
|
||||
GL_UNSIGNED_INT,
|
||||
(void*)0);
|
||||
|
||||
} else {
|
||||
NV2A_GL_DPRINTF(true, "EMPTY NV097_SET_BEGIN_END");
|
||||
assert(false);
|
||||
}
|
||||
pgraph_draw_inline_elements(d);
|
||||
} else {
|
||||
NV2A_GL_DPRINTF(true, "EMPTY NV097_SET_BEGIN_END");
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (pg->opengl_enabled) {
|
||||
/* End of visibility testing */
|
||||
if (pg->zpass_pixel_count_enable) {
|
||||
glEndQuery(GL_SAMPLES_PASSED);
|
||||
}
|
||||
|
||||
NV2A_GL_DGROUP_END();
|
||||
} else {
|
||||
NV2A_GL_DGROUP_BEGIN("NV097_SET_BEGIN_END: 0x%x", parameter);
|
||||
assert(parameter <= NV097_SET_BEGIN_END_OP_POLYGON);
|
||||
|
||||
pgraph_update_surface(d, true, true, depth_test || stencil_test);
|
||||
|
||||
pg->primitive_mode = parameter;
|
||||
|
||||
bool alpha = control_0 & NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE;
|
||||
bool red = control_0 & NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE;
|
||||
bool green = control_0 & NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE;
|
||||
bool blue = control_0 & NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE;
|
||||
glColorMask(red, green, blue, alpha);
|
||||
glDepthMask(!!(control_0 & NV_PGRAPH_CONTROL_0_ZWRITEENABLE));
|
||||
glStencilMask(GET_MASK(control_1,
|
||||
NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE));
|
||||
|
||||
uint32_t blend = pg->regs[NV_PGRAPH_BLEND];
|
||||
if (blend & NV_PGRAPH_BLEND_EN) {
|
||||
glEnable(GL_BLEND);
|
||||
uint32_t sfactor = GET_MASK(blend,
|
||||
NV_PGRAPH_BLEND_SFACTOR);
|
||||
uint32_t dfactor = GET_MASK(blend,
|
||||
NV_PGRAPH_BLEND_DFACTOR);
|
||||
assert(sfactor < ARRAY_SIZE(pgraph_blend_factor_map));
|
||||
assert(dfactor < ARRAY_SIZE(pgraph_blend_factor_map));
|
||||
glBlendFunc(pgraph_blend_factor_map[sfactor],
|
||||
pgraph_blend_factor_map[dfactor]);
|
||||
|
||||
uint32_t equation = GET_MASK(blend,
|
||||
NV_PGRAPH_BLEND_EQN);
|
||||
assert(equation < ARRAY_SIZE(pgraph_blend_equation_map));
|
||||
glBlendEquation(pgraph_blend_equation_map[equation]);
|
||||
|
||||
uint32_t blend_color = pg->regs[NV_PGRAPH_BLENDCOLOR];
|
||||
glBlendColor( ((blend_color >> 16) & 0xFF) / 255.0f, /* red */
|
||||
((blend_color >> 8) & 0xFF) / 255.0f, /* green */
|
||||
(blend_color & 0xFF) / 255.0f, /* blue */
|
||||
((blend_color >> 24) & 0xFF) / 255.0f);/* alpha */
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
/* Face culling */
|
||||
uint32_t setupraster = pg->regs[NV_PGRAPH_SETUPRASTER];
|
||||
if (setupraster
|
||||
& NV_PGRAPH_SETUPRASTER_CULLENABLE) {
|
||||
uint32_t cull_face = GET_MASK(setupraster,
|
||||
NV_PGRAPH_SETUPRASTER_CULLCTRL);
|
||||
assert(cull_face < ARRAY_SIZE(pgraph_cull_face_map));
|
||||
glCullFace(pgraph_cull_face_map[cull_face]);
|
||||
glEnable(GL_CULL_FACE);
|
||||
} else {
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
/* Front-face select */
|
||||
glFrontFace(setupraster
|
||||
& NV_PGRAPH_SETUPRASTER_FRONTFACE
|
||||
? GL_CCW : GL_CW);
|
||||
|
||||
/* Polygon offset */
|
||||
/* FIXME: GL implementation-specific, maybe do this in VS? */
|
||||
if (setupraster &
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE) {
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
} else {
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
}
|
||||
if (setupraster &
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE) {
|
||||
glEnable(GL_POLYGON_OFFSET_LINE);
|
||||
} else {
|
||||
glDisable(GL_POLYGON_OFFSET_LINE);
|
||||
}
|
||||
if (setupraster &
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE) {
|
||||
glEnable(GL_POLYGON_OFFSET_POINT);
|
||||
} else {
|
||||
glDisable(GL_POLYGON_OFFSET_POINT);
|
||||
}
|
||||
if (setupraster &
|
||||
(NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE |
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE |
|
||||
NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) {
|
||||
GLfloat zfactor = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETFACTOR];
|
||||
GLfloat zbias = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETBIAS];
|
||||
glPolygonOffset(zfactor, zbias);
|
||||
}
|
||||
|
||||
/* Depth testing */
|
||||
if (depth_test) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
uint32_t depth_func = GET_MASK(control_0,
|
||||
NV_PGRAPH_CONTROL_0_ZFUNC);
|
||||
assert(depth_func < ARRAY_SIZE(pgraph_depth_func_map));
|
||||
glDepthFunc(pgraph_depth_func_map[depth_func]);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
if (stencil_test) {
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
uint32_t stencil_func = GET_MASK(control_1,
|
||||
NV_PGRAPH_CONTROL_1_STENCIL_FUNC);
|
||||
uint32_t stencil_ref = GET_MASK(control_1,
|
||||
NV_PGRAPH_CONTROL_1_STENCIL_REF);
|
||||
uint32_t func_mask = GET_MASK(control_1,
|
||||
NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ);
|
||||
uint32_t control2 = pg->regs[NV_PGRAPH_CONTROL_2];
|
||||
uint32_t op_fail = GET_MASK(control2,
|
||||
NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL);
|
||||
uint32_t op_zfail = GET_MASK(control2,
|
||||
NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL);
|
||||
uint32_t op_zpass = GET_MASK(control2,
|
||||
NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS);
|
||||
|
||||
assert(stencil_func < ARRAY_SIZE(pgraph_stencil_func_map));
|
||||
assert(op_fail < ARRAY_SIZE(pgraph_stencil_op_map));
|
||||
assert(op_zfail < ARRAY_SIZE(pgraph_stencil_op_map));
|
||||
assert(op_zpass < ARRAY_SIZE(pgraph_stencil_op_map));
|
||||
|
||||
glStencilFunc(
|
||||
pgraph_stencil_func_map[stencil_func],
|
||||
stencil_ref,
|
||||
func_mask);
|
||||
|
||||
glStencilOp(
|
||||
pgraph_stencil_op_map[op_fail],
|
||||
pgraph_stencil_op_map[op_zfail],
|
||||
pgraph_stencil_op_map[op_zpass]);
|
||||
|
||||
} else {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
/* Dither */
|
||||
/* FIXME: GL implementation dependent */
|
||||
if (control_0 &
|
||||
NV_PGRAPH_CONTROL_0_DITHERENABLE) {
|
||||
glEnable(GL_DITHER);
|
||||
} else {
|
||||
glDisable(GL_DITHER);
|
||||
}
|
||||
|
||||
pgraph_bind_shaders(pg);
|
||||
pgraph_bind_textures(d);
|
||||
|
||||
//glDisableVertexAttribArray(NV2A_VERTEX_ATTR_DIFFUSE);
|
||||
//glVertexAttrib4f(NV2A_VERTEX_ATTR_DIFFUSE, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
|
||||
unsigned int width, height;
|
||||
pgraph_get_surface_dimensions(pg, &width, &height);
|
||||
pgraph_apply_anti_aliasing_factor(pg, &width, &height);
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
pg->inline_elements_length = 0;
|
||||
pg->inline_array_length = 0;
|
||||
pg->inline_buffer_length = 0;
|
||||
pg->draw_arrays_length = 0;
|
||||
pg->draw_arrays_max_count = 0;
|
||||
|
||||
/* Visibility testing */
|
||||
if (pg->zpass_pixel_count_enable) {
|
||||
GLuint gl_query;
|
||||
glGenQueries(1, &gl_query);
|
||||
pg->gl_zpass_pixel_count_query_count++;
|
||||
pg->gl_zpass_pixel_count_queries = (GLuint*)g_realloc(
|
||||
pg->gl_zpass_pixel_count_queries,
|
||||
sizeof(GLuint) * pg->gl_zpass_pixel_count_query_count);
|
||||
pg->gl_zpass_pixel_count_queries[
|
||||
pg->gl_zpass_pixel_count_query_count - 1] = gl_query;
|
||||
glBeginQuery(GL_SAMPLES_PASSED, gl_query);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
if (pg->opengl_enabled) {
|
||||
NV2A_GL_DGROUP_BEGIN("NV097_SET_BEGIN_END: 0x%x", parameter);
|
||||
}
|
||||
|
||||
assert(parameter <= NV097_SET_BEGIN_END_OP_POLYGON);
|
||||
|
||||
pg->primitive_mode = parameter;
|
||||
|
||||
pgraph_draw_state_update(d);
|
||||
|
||||
pg->inline_elements_length = 0;
|
||||
pg->inline_array_length = 0;
|
||||
pg->inline_buffer_length = 0;
|
||||
pg->draw_arrays_length = 0;
|
||||
pg->draw_arrays_max_count = 0;
|
||||
}
|
||||
|
||||
pgraph_set_surface_dirty(pg, true, depth_test || stencil_test);
|
||||
|
@ -2805,6 +2882,9 @@ void pgraph_init(NV2AState *d)
|
|||
if (!(pg->opengl_enabled))
|
||||
return;
|
||||
|
||||
/* attach OpenGL render plugins */
|
||||
OpenGL_init_pgraph_plugins();
|
||||
|
||||
/* fire up opengl */
|
||||
|
||||
lockGL(pg);
|
||||
|
|
|
@ -386,7 +386,8 @@ typedef struct PGRAPHState {
|
|||
GLuint gl_inline_array_buffer;
|
||||
|
||||
unsigned int inline_elements_length;
|
||||
uint32_t inline_elements[NV2A_MAX_BATCH_LENGTH];
|
||||
uint16_t inline_elements[NV2A_MAX_BATCH_LENGTH]; // Cxbx-Reloaded TODO : Restore uint32_t once HLE_draw_inline_elements can using that
|
||||
|
||||
|
||||
unsigned int inline_buffer_length;
|
||||
|
||||
|
@ -408,13 +409,17 @@ typedef struct PGRAPHState {
|
|||
static void lockGL_(PGRAPHState* pg, unsigned int line) {
|
||||
//printf("Locking from line %d\n", line);
|
||||
qemu_mutex_lock(&pg->gl_lock);
|
||||
glo_set_current(pg->gl_context);
|
||||
if (pg->opengl_enabled) {
|
||||
glo_set_current(pg->gl_context);
|
||||
}
|
||||
}
|
||||
|
||||
#define unlockGL(x) unlockGL_(x, __LINE__)
|
||||
static void unlockGL_(PGRAPHState* pg, unsigned int line) {
|
||||
//printf("Unlocking from line %d\n", line);
|
||||
glo_set_current(NULL);
|
||||
if (pg->opengl_enabled) {
|
||||
glo_set_current(NULL);
|
||||
}
|
||||
qemu_mutex_unlock(&pg->gl_lock);
|
||||
}
|
||||
|
||||
|
@ -666,6 +671,9 @@ public:
|
|||
void Init();
|
||||
void Reset();
|
||||
|
||||
// State Getter: Used for HLE reading of device state
|
||||
NV2AState* GetDeviceState() { return m_nv2a_state; };
|
||||
|
||||
uint32_t IORead(int barIndex, uint32_t port, unsigned size);
|
||||
void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size);
|
||||
uint32_t BlockRead(const NV2ABlockInfo* block, uint32_t addr, unsigned size);
|
||||
|
|
Loading…
Reference in New Issue