From 7bc95d7a6769326ebb42783bf16091e688504744 Mon Sep 17 00:00:00 2001 From: jackchentwkh Date: Sat, 25 Jun 2022 00:05:04 +0800 Subject: [PATCH 1/8] Add nv2a_vsh_cpu submodule --- .gitmodules | 3 +++ import/nv2a_vsh_cpu | 1 + 2 files changed, 4 insertions(+) create mode 160000 import/nv2a_vsh_cpu diff --git a/.gitmodules b/.gitmodules index 8d4a72ff9..47cf2fe5d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -39,3 +39,6 @@ path = import/libusb url = https://github.com/libusb/libusb shallow = true +[submodule "import/nv2a_vsh_cpu"] + path = import/nv2a_vsh_cpu + url = https://github.com/abaire/nv2a_vsh_cpu.git diff --git a/import/nv2a_vsh_cpu b/import/nv2a_vsh_cpu new file mode 160000 index 000000000..ead0af5de --- /dev/null +++ b/import/nv2a_vsh_cpu @@ -0,0 +1 @@ +Subproject commit ead0af5dee49e408c005151393f8a7dbcd27026c From 6f79b035bdf84c56b0822a84a91d636ca00f4a29 Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Fri, 24 Jun 2022 18:11:06 -0500 Subject: [PATCH 2/8] cmake: include nv2a_vsh_cpu libraries --- CMakeLists.txt | 7 +++++++ projects/cxbxr-emu/CMakeLists.txt | 1 + 2 files changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 141d77afb..4d10741a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,9 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/imgui") add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/libusb") +set(nv2a_vsh_cpu_UNIT_TEST OFF) +add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/nv2a_vsh_cpu" EXCLUDE_FROM_ALL) + # Split the files into group for which project is likely # going to be used for both header and source files. # Then move only specific project files into their @@ -437,6 +440,10 @@ set_target_properties(cxbx cxbxr-ldr cxbxr-emu misc-batch SDL2 subhook libXbSymb PROPERTIES FOLDER Cxbx-Reloaded ) +set_target_properties(nv2a_vsh_emulator nv2a_vsh_disassembler nv2a_vsh_cpu + PROPERTIES FOLDER Cxbx-Reloaded/nv2a_vsh +) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # Configure startup project set_property(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" PROPERTY VS_STARTUP_PROJECT cxbx) diff --git a/projects/cxbxr-emu/CMakeLists.txt b/projects/cxbxr-emu/CMakeLists.txt index de66678c6..adb1888bc 100644 --- a/projects/cxbxr-emu/CMakeLists.txt +++ b/projects/cxbxr-emu/CMakeLists.txt @@ -171,6 +171,7 @@ target_link_libraries(cxbxr-emu SDL2 imgui libusb + nv2a_vsh_emulator ${WINS_LIB} ) From 4d110bad6e968a66c7c6686f4bbf55e968e51921 Mon Sep 17 00:00:00 2001 From: jackchentwkh Date: Sat, 25 Jun 2022 00:18:27 +0800 Subject: [PATCH 3/8] Implement RunVertexStateShader() --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 38 ++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 38ce76540..553e76634 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -78,6 +78,9 @@ #include #include + +#include "nv2a_vsh_emulator.h" + using namespace Microsoft::WRL; XboxRenderStateConverter XboxRenderStates; @@ -8660,6 +8663,8 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetVertexShaderInput) XB_TRMP(D3DDevice_SetVertexShaderInput)(Handle, StreamCount, pStreamInputs); } +extern xbox::dword_xt* GetCxbxVertexShaderSlotPtr(const DWORD SlotIndexAddress); // tmp glue + // ****************************************************************** // * patch: D3DDevice_RunVertexStateShader // ****************************************************************** @@ -8676,8 +8681,39 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_RunVertexStateShader) // If pData is assigned, pData[0..3] is pushed towards nv2a transform data registers // then sends the nv2a a command to launch the vertex shader function located at Address + NV2AState* dev = g_NV2A->GetDeviceState(); + PGRAPHState* pg = &(dev->pgraph); - LOG_UNIMPLEMENTED(); + float vertex_state_shader_v0[4]; + + for (int slot = 0; slot < 4; slot++) + { + if (pData != nullptr) + vertex_state_shader_v0[slot] = pData[slot]; + else + vertex_state_shader_v0[slot] = 0.0f; + } + + int shader_slot = Address; + Nv2aVshProgram program; + + Nv2aVshParseResult result = nv2a_vsh_parse_program( + &program, + //pg->program_data[shader_slot], + GetCxbxVertexShaderSlotPtr(shader_slot), + NV2A_MAX_TRANSFORM_PROGRAM_LENGTH - shader_slot); + assert(result == NV2AVPR_SUCCESS); + + Nv2aVshCPUXVSSExecutionState state_linkage; + Nv2aVshExecutionState state = nv2a_vsh_emu_initialize_xss_execution_state( + &state_linkage, (float*)pg->vsh_constants, pg->vsh_constants_dirty); + memcpy(state_linkage.input_regs, vertex_state_shader_v0, sizeof(vertex_state_shader_v0)); + + nv2a_vsh_emu_execute(&state, &program); + + nv2a_vsh_program_destroy(&program); + + //LOG_UNIMPLEMENTED(); } // ****************************************************************** From ce4f4a07f00cd856219520ebe4bede63da2623d8 Mon Sep 17 00:00:00 2001 From: jackchentwkh Date: Sat, 25 Jun 2022 14:57:00 +0800 Subject: [PATCH 4/8] adopt api nv2a_vsh_emu_initialize_xss_execution_state change from 3 args to 2 args. mark all vertex constants dirty after vertex state shader execution. --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 553e76634..12a0dd84b 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -8706,14 +8706,16 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_RunVertexStateShader) Nv2aVshCPUXVSSExecutionState state_linkage; Nv2aVshExecutionState state = nv2a_vsh_emu_initialize_xss_execution_state( - &state_linkage, (float*)pg->vsh_constants, pg->vsh_constants_dirty); + &state_linkage, (float*)pg->vsh_constants); memcpy(state_linkage.input_regs, vertex_state_shader_v0, sizeof(vertex_state_shader_v0)); nv2a_vsh_emu_execute(&state, &program); nv2a_vsh_program_destroy(&program); - - //LOG_UNIMPLEMENTED(); + //mark all vertex shader constants dirty so later they will be updated by vertex shader processor code. + for(int vshcnt=0;vshcnt< NV2A_VERTEXSHADER_CONSTANTS;vshcnt++) + pg->vsh_constants_dirty[vshcnt] = TRUE; + } // ****************************************************************** From 186b5fa8ee50227a141929e463e031668c233d4d Mon Sep 17 00:00:00 2001 From: jackchentwkh Date: Sat, 25 Jun 2022 16:49:04 +0800 Subject: [PATCH 5/8] adopt api changes of nv2a_vsh_emu_execute_track_context_writes(). using pg->vsh_constants_dirty[] again. --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 12a0dd84b..24526071d 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -8709,13 +8709,9 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_RunVertexStateShader) &state_linkage, (float*)pg->vsh_constants); memcpy(state_linkage.input_regs, vertex_state_shader_v0, sizeof(vertex_state_shader_v0)); - nv2a_vsh_emu_execute(&state, &program); + nv2a_vsh_emu_execute_track_context_writes(&state, &program, pg->vsh_constants_dirty); - nv2a_vsh_program_destroy(&program); - //mark all vertex shader constants dirty so later they will be updated by vertex shader processor code. - for(int vshcnt=0;vshcnt< NV2A_VERTEXSHADER_CONSTANTS;vshcnt++) - pg->vsh_constants_dirty[vshcnt] = TRUE; - + nv2a_vsh_program_destroy(&program); } // ****************************************************************** From c981ff23b169bff7820b1174106c7d8c8efeaf9c Mon Sep 17 00:00:00 2001 From: jackchentwkh Date: Sat, 25 Jun 2022 17:02:13 +0800 Subject: [PATCH 6/8] Correction of v0.xyzw assignment. --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 24526071d..1742adb13 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -8685,14 +8685,15 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_RunVertexStateShader) PGRAPHState* pg = &(dev->pgraph); float vertex_state_shader_v0[4]; - - for (int slot = 0; slot < 4; slot++) - { - if (pData != nullptr) - vertex_state_shader_v0[slot] = pData[slot]; - else + if (pData != nullptr) + //if pData != nullptr, then it contents v0.xyzw, we shall copy the binary content directly. + memcpy(vertex_state_shader_v0,pData, sizeof(vertex_state_shader_v0)); + else + //for pData == nullptr, this data is not supposed to be used. but we assign v0.xyzw 0.0f in each component just in case. + for (int slot = 0; slot < 4; slot++) + { vertex_state_shader_v0[slot] = 0.0f; - } + } int shader_slot = Address; Nv2aVshProgram program; From 4c5995af0cbc0db92f36269701f9fb45f74561a5 Mon Sep 17 00:00:00 2001 From: jackchentwkh Date: Sun, 26 Jun 2022 10:36:53 +0800 Subject: [PATCH 7/8] using memset for vertex_state_shader_v0[] init. --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 1742adb13..e1de629eb 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -8687,13 +8687,10 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_RunVertexStateShader) float vertex_state_shader_v0[4]; if (pData != nullptr) //if pData != nullptr, then it contents v0.xyzw, we shall copy the binary content directly. - memcpy(vertex_state_shader_v0,pData, sizeof(vertex_state_shader_v0)); + memcpy(vertex_state_shader_v0, pData, sizeof(vertex_state_shader_v0)); else //for pData == nullptr, this data is not supposed to be used. but we assign v0.xyzw 0.0f in each component just in case. - for (int slot = 0; slot < 4; slot++) - { - vertex_state_shader_v0[slot] = 0.0f; - } + std::memset(vertex_state_shader_v0, 0, sizeof(vertex_state_shader_v0)); int shader_slot = Address; Nv2aVshProgram program; From 8546d7c10d0c7b92f6405d40f7caabc3b58aea5d Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Thu, 30 Jun 2022 11:33:29 +0200 Subject: [PATCH 8/8] EMUPATCH(D3DDevice_RunVertexStateShader): added LOG_TEST_CASE precondition checks, replacing an assert simplified implementation (no need for an intermediate variable) commented some notes and future suggestions --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 44 +++++++++++++---------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index e1de629eb..fe0877296 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -8681,35 +8681,41 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_RunVertexStateShader) // If pData is assigned, pData[0..3] is pushed towards nv2a transform data registers // then sends the nv2a a command to launch the vertex shader function located at Address + if (Address >= NV2A_MAX_TRANSFORM_PROGRAM_LENGTH) { + LOG_TEST_CASE("Address out of bounds"); + return; + } + NV2AState* dev = g_NV2A->GetDeviceState(); PGRAPHState* pg = &(dev->pgraph); - float vertex_state_shader_v0[4]; - if (pData != nullptr) - //if pData != nullptr, then it contents v0.xyzw, we shall copy the binary content directly. - memcpy(vertex_state_shader_v0, pData, sizeof(vertex_state_shader_v0)); - else - //for pData == nullptr, this data is not supposed to be used. but we assign v0.xyzw 0.0f in each component just in case. - std::memset(vertex_state_shader_v0, 0, sizeof(vertex_state_shader_v0)); - - int shader_slot = Address; - Nv2aVshProgram program; - + Nv2aVshProgram program = {}; // Note: This nulls program.steps + // TODO : Retain program globally and perform nv2a_vsh_parse_program only when + // the address-range we're about to emulate was modified since last parse. + // TODO : As a suggestion for this, parse all NV2A_MAX_TRANSFORM_PROGRAM_LENGTH slots, + // and here just point program.steps to global vsh_program_steps[Address]. Nv2aVshParseResult result = nv2a_vsh_parse_program( - &program, - //pg->program_data[shader_slot], - GetCxbxVertexShaderSlotPtr(shader_slot), - NV2A_MAX_TRANSFORM_PROGRAM_LENGTH - shader_slot); - assert(result == NV2AVPR_SUCCESS); + &program, // Note : program.steps will be malloc'ed + GetCxbxVertexShaderSlotPtr(Address), // TODO : At some point, use pg->program_data[Address] here instead + NV2A_MAX_TRANSFORM_PROGRAM_LENGTH - Address); + if (result != NV2AVPR_SUCCESS) { + LOG_TEST_CASE("nv2a_vsh_parse_program failed"); + // TODO : Dump Nv2aVshParseResult as string and program for debugging purposes + return; + } Nv2aVshCPUXVSSExecutionState state_linkage; Nv2aVshExecutionState state = nv2a_vsh_emu_initialize_xss_execution_state( - &state_linkage, (float*)pg->vsh_constants); - memcpy(state_linkage.input_regs, vertex_state_shader_v0, sizeof(vertex_state_shader_v0)); + &state_linkage, (float*)pg->vsh_constants); // Note : This wil memset(state_linkage, 0) + if (pData != nullptr) + //if pData != nullptr, then it contains v0.xyzw, we shall copy the binary content directly. + memcpy(state_linkage.input_regs, pData, sizeof(state_linkage.input_regs)); nv2a_vsh_emu_execute_track_context_writes(&state, &program, pg->vsh_constants_dirty); + // Note: Above emulation's primary purpose is to update pg->vsh_constants and pg->vsh_constants_dirty + // therefor, nothing else needs to be done here, other than to cleanup - nv2a_vsh_program_destroy(&program); + nv2a_vsh_program_destroy(&program); // Note: program.steps will be free'ed } // ******************************************************************