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:
PatrickvL 2018-09-07 10:48:19 +02:00
parent b46a82b23a
commit 7ccd90e2bd
6 changed files with 493 additions and 566 deletions

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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);

View File

@ -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);