Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 38 additions & 31 deletions src/d3d8/d3d8_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ namespace dxvk {
, m_behaviorFlags(BehaviorFlags)
, m_multithread(BehaviorFlags & D3DCREATE_MULTITHREADED) {
// Get the bridge interface to D3D9.
if (FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast<void**>(&m_bridge)))) {
if (unlikely(FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast<void**>(&m_bridge))))) {
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
}

Expand Down Expand Up @@ -1141,31 +1141,34 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE D3D8Device::SetViewport(const D3DVIEWPORT8* pViewport) {
D3D8DeviceLock lock = LockDevice();

if (likely(pViewport != nullptr)) {
// We need a valid render target to validate the viewport
if (unlikely(m_renderTarget == nullptr))
return D3DERR_INVALIDCALL;
// Outright crashes on native, but let's be
// somewhat more elegant about it.
if (unlikely(pViewport == nullptr))
return D3DERR_INVALIDCALL;

D3DSURFACE_DESC rtDesc;
HRESULT res = m_renderTarget->GetDesc(&rtDesc);

// D3D8 will fail when setting a viewport that's outside of the
// current render target, although this apparently works in D3D9
if (likely(SUCCEEDED(res)) &&
unlikely(pViewport->X + pViewport->Width > rtDesc.Width ||
pViewport->Y + pViewport->Height > rtDesc.Height)) {
// On Linux/Wine and in windowed mode, we can get in situations
// where the actual render target dimensions are off by one
// pixel to what the game sets them to. Allow this corner case
// to skip the validation, in order to prevent issues.
const bool isOnePixelWider = pViewport->X + pViewport->Width == rtDesc.Width + 1;
const bool isOnePixelTaller = pViewport->Y + pViewport->Height == rtDesc.Height + 1;

if (m_presentParams.Windowed && (isOnePixelWider || isOnePixelTaller)) {
Logger::debug("D3D8Device::SetViewport: Viewport exceeds render target dimensions by one pixel");
} else {
return D3DERR_INVALIDCALL;
}
// We need a valid render target to validate the viewport
if (unlikely(m_renderTarget == nullptr))
return D3DERR_INVALIDCALL;

D3DSURFACE_DESC rtDesc;
HRESULT res = m_renderTarget->GetDesc(&rtDesc);

// D3D8 will fail when setting a viewport that's outside of the
// current render target, although this apparently works in D3D9
if (likely(SUCCEEDED(res)) &&
unlikely(pViewport->X + pViewport->Width > rtDesc.Width ||
pViewport->Y + pViewport->Height > rtDesc.Height)) {
// On Linux/Wine and in windowed mode, we can get in situations
// where the actual render target dimensions are off by one
// pixel to what the game sets them to. Allow this corner case
// to skip the validation, in order to prevent issues.
const bool isOnePixelWider = pViewport->X + pViewport->Width == rtDesc.Width + 1;
const bool isOnePixelTaller = pViewport->Y + pViewport->Height == rtDesc.Height + 1;

if (unlikely(m_presentParams.Windowed && (isOnePixelWider || isOnePixelTaller))) {
Logger::debug("D3D8Device::SetViewport: Viewport exceeds render target dimensions by one pixel");
} else {
return D3DERR_INVALIDCALL;
}
}

Expand Down Expand Up @@ -1226,18 +1229,22 @@ namespace dxvk {
if (unlikely(ShouldRecord()))
return D3DERR_INVALIDCALL;

D3D8StateBlockType stateBlockType = ConvertStateBlockType(Type);

if (unlikely(stateBlockType == D3D8StateBlockType::Unknown)) {
Logger::warn(str::format("D3D8Device::CreateStateBlock: Invalid state block type: ", Type));
return D3DERR_INVALIDCALL;
}

Com<d3d9::IDirect3DStateBlock9> pStateBlock9;
HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9);

if (likely(SUCCEEDED(res))) {
m_token++;
auto stateBlockIterPair = m_stateBlocks.emplace(std::piecewise_construct,
std::forward_as_tuple(m_token),
std::forward_as_tuple(this, Type, pStateBlock9.ptr()));
m_stateBlocks.emplace(std::piecewise_construct,
std::forward_as_tuple(m_token),
std::forward_as_tuple(this, stateBlockType, pStateBlock9.ptr()));
*pToken = m_token;

// D3D8 state blocks automatically capture state on creation.
stateBlockIterPair.first->second.Capture();
}

return res;
Expand Down
38 changes: 23 additions & 15 deletions src/d3d8/d3d8_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace dxvk {
D3D8Interface::D3D8Interface()
: m_d3d9(d3d9::Direct3DCreate9(D3D_SDK_VERSION)) {
// Get the bridge interface to D3D9.
if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast<void**>(&m_bridge)))) {
if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast<void**>(&m_bridge))))) {
throw DxvkError("D3D8Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
}

Expand Down Expand Up @@ -75,21 +75,22 @@ namespace dxvk {
d3d9::D3DADAPTER_IDENTIFIER9 identifier9;
HRESULT res = m_d3d9->GetAdapterIdentifier(Adapter, Flags, &identifier9);

if (likely(SUCCEEDED(res))) {
strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING);
strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING);
if (unlikely(FAILED(res)))
return res;

pIdentifier->DriverVersion = identifier9.DriverVersion;
pIdentifier->VendorId = identifier9.VendorId;
pIdentifier->DeviceId = identifier9.DeviceId;
pIdentifier->SubSysId = identifier9.SubSysId;
pIdentifier->Revision = identifier9.Revision;
pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier;
strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING);
strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING);

pIdentifier->WHQLLevel = identifier9.WHQLLevel;
}
pIdentifier->DriverVersion = identifier9.DriverVersion;
pIdentifier->VendorId = identifier9.VendorId;
pIdentifier->DeviceId = identifier9.DeviceId;
pIdentifier->SubSysId = identifier9.SubSysId;
pIdentifier->Revision = identifier9.Revision;
pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier;

return res;
pIdentifier->WHQLLevel = identifier9.WHQLLevel;

return D3D_OK;
}

HRESULT __stdcall D3D8Interface::EnumAdapterModes(
Expand Down Expand Up @@ -136,14 +137,21 @@ namespace dxvk {
&pDevice9
);

if (likely(SUCCEEDED(res)))
if (unlikely(FAILED(res)))
return res;

try {
*ppReturnedDeviceInterface = ref(new D3D8Device(
this, std::move(pDevice9),
DeviceType, hFocusWindow, BehaviorFlags,
pPresentationParameters
));
} catch (const DxvkError& e) {
Logger::err(e.message());
return D3DERR_NOTAVAILABLE;
}

return res;
return D3D_OK;
}

HRESULT D3D8Interface::ValidatePresentationParameters(
Expand Down
9 changes: 8 additions & 1 deletion src/d3d8/d3d8_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ namespace dxvk {
if (!ppDirect3D8)
return D3DERR_INVALIDCALL;

*ppDirect3D8 = ref(new D3D8Interface());
try {
*ppDirect3D8 = ref(new D3D8Interface());
} catch (const DxvkError& e) {
Logger::err(e.message());
*ppDirect3D8 = nullptr;
return D3DERR_NOTAVAILABLE;
}

return D3D_OK;
}

Expand Down
114 changes: 67 additions & 47 deletions src/d3d8/d3d8_state_block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,43 @@ namespace dxvk {

D3D8StateBlock::D3D8StateBlock(
D3D8Device* pDevice,
D3DSTATEBLOCKTYPE Type,
D3D8StateBlockType Type,
Com<d3d9::IDirect3DStateBlock9>&& pStateBlock)
: m_device(pDevice)
, m_stateBlock(std::move(pStateBlock))
, m_type(Type)
, m_isSWVP(pDevice->GetD3D9()->GetSoftwareVertexProcessing()) {
if (Type == D3DSBT_VERTEXSTATE || Type == D3DSBT_ALL) {
, m_stateBlock(std::move(pStateBlock)) {
if (Type == D3D8StateBlockType::All) {
m_captures.flags.set(D3D8CapturedStateFlag::Indices);
m_captures.flags.set(D3D8CapturedStateFlag::SWVP);

m_captures.flags.set(D3D8CapturedStateFlag::VertexBuffers);
m_captures.streams.setAll();

m_captures.flags.set(D3D8CapturedStateFlag::Textures);
m_captures.textures.setAll();
}

if (Type == D3D8StateBlockType::VertexState || Type == D3D8StateBlockType::All) {
// Lights, D3DTSS_TEXCOORDINDEX and D3DTSS_TEXTURETRANSFORMFLAGS,
// vertex shader, VS constants, and various render states.
m_capture.vs = true;
m_captures.flags.set(D3D8CapturedStateFlag::VertexShader);
}

if (Type == D3DSBT_PIXELSTATE || Type == D3DSBT_ALL) {
if (Type == D3D8StateBlockType::PixelState || Type == D3D8StateBlockType::All) {
// Pixel shader, PS constants, and various RS/TSS states.
m_capture.ps = true;
m_captures.flags.set(D3D8CapturedStateFlag::PixelShader);
}

if (Type == D3DSBT_ALL) {
m_capture.indices = true;
m_capture.swvp = true;
m_capture.textures.setAll();
m_capture.streams.setAll();
}
m_state.textures.fill(nullptr);
m_state.streams.fill(D3D8VBOP());

m_textures.fill(nullptr);
m_streams.fill(D3D8VBOP());
// Automatically capture state on creation via D3D8Device::CreateStateBlock.
if (Type != D3D8StateBlockType::None)
Capture();
}

// Construct a state block without a D3D9 object
D3D8StateBlock::D3D8StateBlock(D3D8Device* pDevice)
: D3D8StateBlock(pDevice, D3DSTATEBLOCKTYPE(0), nullptr) {
: D3D8StateBlock(pDevice, D3D8StateBlockType::None, nullptr) {
}

// Attach a D3D9 object to a state block that doesn't have one yet
Expand All @@ -51,31 +57,38 @@ namespace dxvk {
if (unlikely(m_stateBlock == nullptr))
return D3DERR_INVALIDCALL;

if (m_capture.vs) m_device->GetVertexShader(&m_vertexShader);
if (m_capture.ps) m_device->GetPixelShader(&m_pixelShader);
if (m_captures.flags.test(D3D8CapturedStateFlag::Indices)) {
m_state.baseVertexIndex = m_device->m_baseVertexIndex;
m_state.indices = m_device->m_indices.ptr();
}

for (DWORD stage = 0; stage < m_textures.size(); stage++) {
if (m_capture.textures.get(stage))
m_textures[stage] = m_device->m_textures[stage].ptr();
if (m_captures.flags.test(D3D8CapturedStateFlag::SWVP)) {
DWORD swvpState;
m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, &swvpState);
m_state.isSWVP = static_cast<bool>(swvpState);
}

for (DWORD stream = 0; stream < m_streams.size(); stream++) {
if (m_capture.streams.get(stream)) {
m_streams[stream].buffer = m_device->m_streams[stream].buffer.ptr();
m_streams[stream].stride = m_device->m_streams[stream].stride;
if (m_captures.flags.test(D3D8CapturedStateFlag::VertexBuffers)) {
for (DWORD stream = 0; stream < m_state.streams.size(); stream++) {
if (m_captures.streams.get(stream)) {
m_state.streams[stream].buffer = m_device->m_streams[stream].buffer.ptr();
m_state.streams[stream].stride = m_device->m_streams[stream].stride;
}
}
}

if (m_capture.indices) {
m_baseVertexIndex = m_device->m_baseVertexIndex;
m_indices = m_device->m_indices.ptr();
if (m_captures.flags.test(D3D8CapturedStateFlag::Textures)) {
for (DWORD stage = 0; stage < m_state.textures.size(); stage++) {
if (m_captures.textures.get(stage))
m_state.textures[stage] = m_device->m_textures[stage].ptr();
}
}

if (m_capture.swvp) {
DWORD swvpState;
m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, &swvpState);
m_isSWVP = static_cast<bool>(swvpState);
}
if (m_captures.flags.test(D3D8CapturedStateFlag::VertexShader))
m_device->GetVertexShader(&m_state.vertexShaderHandle);

if (m_captures.flags.test(D3D8CapturedStateFlag::PixelShader))
m_device->GetPixelShader(&m_state.pixelShaderHandle);

return m_stateBlock->Capture();
}
Expand All @@ -86,25 +99,32 @@ namespace dxvk {

HRESULT res = m_stateBlock->Apply();

if (m_capture.vs) m_device->SetVertexShader(m_vertexShader);
if (m_capture.ps) m_device->SetPixelShader(m_pixelShader);
if (m_captures.flags.test(D3D8CapturedStateFlag::Indices))
m_device->SetIndices(m_state.indices, m_state.baseVertexIndex);

for (DWORD stage = 0; stage < m_textures.size(); stage++) {
if (m_capture.textures.get(stage))
m_device->SetTexture(stage, m_textures[stage]);
// This was a very easy footgun for D3D8 applications.
if (m_captures.flags.test(D3D8CapturedStateFlag::SWVP))
m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, static_cast<DWORD>(m_state.isSWVP));

if (m_captures.flags.test(D3D8CapturedStateFlag::VertexBuffers)) {
for (DWORD stream = 0; stream < m_state.streams.size(); stream++) {
if (m_captures.streams.get(stream))
m_device->SetStreamSource(stream, m_state.streams[stream].buffer, m_state.streams[stream].stride);
}
}

for (DWORD stream = 0; stream < m_streams.size(); stream++) {
if (m_capture.streams.get(stream))
m_device->SetStreamSource(stream, m_streams[stream].buffer, m_streams[stream].stride);
if (m_captures.flags.test(D3D8CapturedStateFlag::Textures)) {
for (DWORD stage = 0; stage < m_state.textures.size(); stage++) {
if (m_captures.textures.get(stage))
m_device->SetTexture(stage, m_state.textures[stage]);
}
}

if (m_capture.indices)
m_device->SetIndices(m_indices, m_baseVertexIndex);
if (m_captures.flags.test(D3D8CapturedStateFlag::VertexShader))
m_device->SetVertexShader(m_state.vertexShaderHandle);

// This was a very easy footgun for D3D8 applications.
if (m_capture.swvp)
m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, static_cast<DWORD>(m_isSWVP));
if (m_captures.flags.test(D3D8CapturedStateFlag::PixelShader))
m_device->SetPixelShader(m_state.pixelShaderHandle);

return res;
}
Expand Down
Loading