diff --git a/DDrawCompat/Config/Config.cpp b/DDrawCompat/Config/Config.cpp index 9b3f018..97f244a 100644 --- a/DDrawCompat/Config/Config.cpp +++ b/DDrawCompat/Config/Config.cpp @@ -12,6 +12,7 @@ namespace Config Settings::DisplayResolution displayResolution; Settings::RenderColorDepth renderColorDepth; Settings::ResolutionScale resolutionScale; + Settings::SpriteTexCoord spriteTexCoord; Settings::SupportedResolutions supportedResolutions; Settings::TextureFilter textureFilter; Settings::ThreadPriorityBoost threadPriorityBoost; diff --git a/DDrawCompat/Config/Config.h b/DDrawCompat/Config/Config.h index 3848837..c8fe33c 100644 --- a/DDrawCompat/Config/Config.h +++ b/DDrawCompat/Config/Config.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ namespace Config extern Settings::DisplayResolution displayResolution; extern Settings::RenderColorDepth renderColorDepth; extern Settings::ResolutionScale resolutionScale; + extern Settings::SpriteTexCoord spriteTexCoord; extern Settings::SupportedResolutions supportedResolutions; extern Settings::TextureFilter textureFilter; extern Settings::ThreadPriorityBoost threadPriorityBoost; diff --git a/DDrawCompat/Config/Settings/SpriteTexCoord.cpp b/DDrawCompat/Config/Settings/SpriteTexCoord.cpp new file mode 100644 index 0000000..4e2e974 --- /dev/null +++ b/DDrawCompat/Config/Settings/SpriteTexCoord.cpp @@ -0,0 +1,25 @@ +#include + +namespace Config +{ + namespace Settings + { + SpriteTexCoord::SpriteTexCoord() + : MappedSetting("SpriteTexCoord", "app", { + {"app", APP}, + {"clamp", CLAMP}, + {"clampall", CLAMPALL}, + {"round", ROUND}}) + { + } + + Setting::ParamInfo SpriteTexCoord::getParamInfo() const + { + if (ROUND == m_value) + { + return { "Offset", -50, 50, 0, m_param }; + } + return {}; + } + } +} diff --git a/DDrawCompat/Config/Settings/SpriteTexCoord.h b/DDrawCompat/Config/Settings/SpriteTexCoord.h new file mode 100644 index 0000000..23ba2b4 --- /dev/null +++ b/DDrawCompat/Config/Settings/SpriteTexCoord.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace Config +{ + namespace Settings + { + class SpriteTexCoord : public MappedSetting + { + public: + static const UINT APP = 0; + static const UINT CLAMP = 1; + static const UINT CLAMPALL = 2; + static const UINT ROUND = 3; + + SpriteTexCoord(); + + virtual ParamInfo getParamInfo() const override; + }; + } +} diff --git a/DDrawCompat/D3dDdi/Device.cpp b/DDrawCompat/D3dDdi/Device.cpp index f97b06f..57b2272 100644 --- a/DDrawCompat/D3dDdi/Device.cpp +++ b/DDrawCompat/D3dDdi/Device.cpp @@ -242,7 +242,13 @@ namespace D3dDdi HRESULT result = m_origVtable.pfnDestroyResource(m_device, resource); if (SUCCEEDED(result)) { - m_resources.erase(resource); + auto it = m_resources.find(resource); + Resource* res = nullptr; + if (it != m_resources.end()) + { + res = it->second.get(); + m_resources.erase(it); + } if (resource == m_sharedPrimary) { m_sharedPrimary = nullptr; @@ -253,7 +259,7 @@ namespace D3dDdi g_gdiResource = nullptr; } m_drawPrimitive.removeSysMemVertexBuffer(resource); - m_state.onDestroyResource(resource); + m_state.onDestroyResource(res, resource); } return result; diff --git a/DDrawCompat/D3dDdi/DeviceState.cpp b/DDrawCompat/D3dDdi/DeviceState.cpp index 01ae673..341b5ac 100644 --- a/DDrawCompat/D3dDdi/DeviceState.cpp +++ b/DDrawCompat/D3dDdi/DeviceState.cpp @@ -14,34 +14,6 @@ namespace { const HANDLE DELETED_RESOURCE = reinterpret_cast(0xBAADBAAD); - - UINT mapRsValue(D3DDDIRENDERSTATETYPE state, UINT value) - { - if (state >= D3DDDIRS_WRAP0 && state <= D3DDDIRS_WRAP7) - { - return value & (D3DWRAPCOORD_0 | D3DWRAPCOORD_1 | D3DWRAPCOORD_2 | D3DWRAPCOORD_3); - } - - return value; - } - - UINT mapTssValue(D3DDDITEXTURESTAGESTATETYPE type, UINT value) - { - switch (type) - { - case D3DDDITSS_MAGFILTER: - case D3DDDITSS_MINFILTER: - return D3DTEXF_NONE == Config::textureFilter.getFilter() ? value : Config::textureFilter.getFilter(); - - case D3DDDITSS_MIPFILTER: - return D3DTEXF_NONE == Config::textureFilter.getMipFilter() ? value : Config::textureFilter.getMipFilter(); - - case D3DDDITSS_MAXANISOTROPY: - return D3DTEXF_NONE == Config::textureFilter.getFilter() ? value : Config::textureFilter.getMaxAnisotropy(); - } - - return value; - } } namespace D3dDdi @@ -74,6 +46,9 @@ namespace D3dDdi , m_maxChangedTextureStage(0) , m_changedTextureStageStates{} , m_vsVertexFixup(createVertexShader(g_vsVertexFixup)) + , m_textureResource{} + , m_isLocked(false) + , m_spriteMode(false) { const UINT D3DBLENDOP_ADD = 1; const UINT UNINITIALIZED_STATE = 0xBAADBAAD; @@ -167,6 +142,12 @@ namespace D3dDdi { m_current.textureStageState[i].fill(UNINITIALIZED_STATE); m_current.textureStageState[i][D3DDDITSS_TEXCOORDINDEX] = i; + + // When ADDRESSU or ADDRESSV is set to CLAMP, their value is overridden by D3DTSS_ADDRESS. + // Setting this to CLAMP makes them behave as expected, instead of as WRAP, + // which would be the default init value set by the runtime. + m_current.textureStageState[i][D3DTSS_ADDRESS] = D3DTADDRESS_CLAMP; + m_current.textureStageState[i][D3DDDITSS_ADDRESSU] = D3DTADDRESS_WRAP; m_current.textureStageState[i][D3DDDITSS_ADDRESSV] = D3DTADDRESS_WRAP; m_current.textureStageState[i][D3DDDITSS_BORDERCOLOR] = 0; @@ -232,9 +213,19 @@ namespace D3dDdi return result; } + void DeviceState::disableTextureClamp(UINT stage) + { + auto resource = getTextureResource(stage); + resource->disableClamp(); + m_changedStates |= CS_TEXTURE_STAGE; + m_changedTextureStageStates[stage].set(D3DDDITSS_ADDRESSU); + m_changedTextureStageStates[stage].set(D3DDDITSS_ADDRESSV); + m_maxChangedTextureStage = max(stage, m_maxChangedTextureStage); + } + void DeviceState::flush() { - if (0 == m_changedStates) + if (0 == m_changedStates || m_isLocked) { return; } @@ -264,25 +255,96 @@ namespace D3dDdi m_maxChangedTextureStage = 0; } - void DeviceState::onDestroyResource(HANDLE resource) + Resource* DeviceState::getTextureResource(UINT stage) + { + if (!m_textureResource[stage] || *m_textureResource[stage] != m_app.textures[stage]) + { + m_textureResource[stage] = m_device.getResource(m_app.textures[stage]); + } + return m_textureResource[stage]; + } + + const DeviceState::VertexDecl& DeviceState::getVertexDecl() const + { + static const VertexDecl emptyDecl = {}; + auto it = m_vertexShaderDecls.find(m_app.vertexShaderDecl); + return it != m_vertexShaderDecls.end() ? it->second : emptyDecl; + } + + UINT DeviceState::mapRsValue(D3DDDIRENDERSTATETYPE state, UINT value) + { + if (state >= D3DDDIRS_WRAP0 && state <= D3DDDIRS_WRAP7) + { + return value & (D3DWRAPCOORD_0 | D3DWRAPCOORD_1 | D3DWRAPCOORD_2 | D3DWRAPCOORD_3); + } + if (D3DDDIRS_MULTISAMPLEANTIALIAS == state) + { + return 0 != value && !m_spriteMode; + } + + return value; + } + + UINT DeviceState::mapTssValue(UINT stage, D3DDDITEXTURESTAGESTATETYPE state, UINT value) + { + switch (state) + { + case D3DDDITSS_ADDRESSU: + case D3DDDITSS_ADDRESSV: + if (m_spriteMode && D3DTADDRESS_CLAMP != value) + { + if (Config::Settings::SpriteTexCoord::CLAMP == Config::spriteTexCoord.get()) + { + auto resource = getTextureResource(stage); + if (resource && resource->isClampable()) + { + return D3DTADDRESS_CLAMP; + } + } + else if (Config::Settings::SpriteTexCoord::CLAMPALL == Config::spriteTexCoord.get()) + { + return D3DTADDRESS_CLAMP; + } + } + return value; + + case D3DDDITSS_MAGFILTER: + case D3DDDITSS_MINFILTER: + return D3DTEXF_NONE == Config::textureFilter.getFilter() ? value : Config::textureFilter.getFilter(); + + case D3DDDITSS_MIPFILTER: + return D3DTEXF_NONE == Config::textureFilter.getMipFilter() ? value : Config::textureFilter.getMipFilter(); + + case D3DDDITSS_MAXANISOTROPY: + return D3DTEXF_NONE == Config::textureFilter.getFilter() ? value : Config::textureFilter.getMaxAnisotropy(); + } + + return value; + } + + void DeviceState::onDestroyResource(D3dDdi::Resource* resource, HANDLE resourceHandle) { for (UINT i = 0; i < m_current.textures.size(); ++i) { - if (m_current.textures[i] == resource) + if (m_current.textures[i] == resourceHandle) { m_current.textures[i] = DELETED_RESOURCE; } - if (m_app.textures[i] == resource) + if (m_app.textures[i] == resourceHandle) { pfnSetTexture(i, nullptr); } + if (m_textureResource[i] == resource) + { + m_textureResource[i] = nullptr; + } } - removeResource(resource, &State::renderTarget, &D3DDDIARG_SETRENDERTARGET::hRenderTarget, + removeResource(resourceHandle, &State::renderTarget, &D3DDDIARG_SETRENDERTARGET::hRenderTarget, &DeviceState::pfnSetRenderTarget); - removeResource(resource, &State::depthStencil, &D3DDDIARG_SETDEPTHSTENCIL::hZBuffer, + removeResource(resourceHandle, &State::depthStencil, &D3DDDIARG_SETDEPTHSTENCIL::hZBuffer, &DeviceState::pfnSetDepthStencil); - removeResource(resource, &State::streamSource, &D3DDDIARG_SETSTREAMSOURCE::hVertexBuffer, + removeResource(resourceHandle, &State::streamSource, &D3DDDIARG_SETSTREAMSOURCE::hVertexBuffer, &DeviceState::pfnSetStreamSource); } @@ -293,22 +355,37 @@ namespace D3dDdi LOG_DEBUG << Compat::array(vertexElements, data->NumVertexElements); const UINT D3DDECLUSAGE_POSITION = 0; + const UINT D3DDECLUSAGE_TEXCOORD = 5; const UINT D3DDECLUSAGE_POSITIONT = 9; + + std::vector ve(vertexElements, vertexElements + data->NumVertexElements); + VertexDecl decl = {}; + decl.elements = ve; + for (UINT i = 0; i < data->NumVertexElements; ++i) { - if (D3DDECLUSAGE_POSITIONT == vertexElements[i].Usage) + if (D3DDECLUSAGE_TEXCOORD == vertexElements[i].Usage) { - std::vector ve(vertexElements, vertexElements + data->NumVertexElements); - ve[i].Usage = D3DDECLUSAGE_POSITION; - HRESULT result = m_device.getOrigVtable().pfnCreateVertexShaderDecl(m_device, data, ve.data()); - if (SUCCEEDED(result)) + decl.texCoordOffset[vertexElements[i].UsageIndex] = vertexElements[i].Offset; + decl.texCoordType[vertexElements[i].UsageIndex] = vertexElements[i].Type; + if (vertexElements[i].UsageIndex >= decl.textureStageCount) { - m_swVertexShaderDecls.insert(data->ShaderHandle); + decl.textureStageCount = vertexElements[i].UsageIndex + 1; } - return result; + } + else if (D3DDECLUSAGE_POSITIONT == vertexElements[i].Usage) + { + ve[i].Usage = D3DDECLUSAGE_POSITION; + decl.isTransformed = true; } } - return m_device.getOrigVtable().pfnCreateVertexShaderDecl(m_device, data, vertexElements); + + HRESULT result = m_device.getOrigVtable().pfnCreateVertexShaderDecl(m_device, data, ve.data()); + if (SUCCEEDED(result)) + { + m_vertexShaderDecls[data->ShaderHandle] = decl; + } + return result; } HRESULT DeviceState::pfnDeletePixelShader(HANDLE shader) @@ -321,7 +398,7 @@ namespace D3dDdi HRESULT result = deleteShader(shader, &State::vertexShaderDecl, m_device.getOrigVtable().pfnDeleteVertexShaderDecl); if (SUCCEEDED(result)) { - m_swVertexShaderDecls.erase(shader); + m_vertexShaderDecls.erase(shader); } return result; } @@ -397,16 +474,29 @@ namespace D3dDdi { m_app.textures[stage] = texture; m_changedStates |= CS_TEXTURE_STAGE; + m_changedTextureStageStates[stage].set(D3DDDITSS_ADDRESSU); + m_changedTextureStageStates[stage].set(D3DDDITSS_ADDRESSV); m_maxChangedTextureStage = max(stage, m_maxChangedTextureStage); return S_OK; } HRESULT DeviceState::pfnSetTextureStageState(const D3DDDIARG_TEXTURESTAGESTATE* data) { + if (D3DTSS_ADDRESS == data->State) + { + m_app.textureStageState[data->Stage][D3DDDITSS_ADDRESSU] = data->Value; + m_app.textureStageState[data->Stage][D3DDDITSS_ADDRESSV] = data->Value; + m_changedTextureStageStates[data->Stage].set(D3DDDITSS_ADDRESSU); + m_changedTextureStageStates[data->Stage].set(D3DDDITSS_ADDRESSV); + m_maxChangedTextureStage = max(data->Stage, m_maxChangedTextureStage); + return S_OK; + } + if (D3DDDITSS_TEXTURECOLORKEYVAL == data->State) { m_app.textureStageState[data->Stage][D3DDDITSS_DISABLETEXTURECOLORKEY] = FALSE; } + m_app.textureStageState[data->Stage][data->State] = data->Value; m_changedTextureStageStates[data->Stage].set(data->State); m_maxChangedTextureStage = max(data->Stage, m_maxChangedTextureStage); @@ -544,6 +634,27 @@ namespace D3dDdi return true; } + void DeviceState::setSpriteMode(bool spriteMode) + { + if (spriteMode != m_spriteMode) + { + m_spriteMode = spriteMode; + m_changedStates |= CS_RENDER_STATE | CS_TEXTURE_STAGE; + m_changedRenderStates.set(D3DDDIRS_MULTISAMPLEANTIALIAS); + if (Config::Settings::SpriteTexCoord::ROUND == Config::spriteTexCoord.get()) + { + m_changedTextureStageStates[0].set(D3DDDITSS_ADDRESSU); + m_changedTextureStageStates[0].set(D3DDDITSS_ADDRESSV); + } + + D3DDDIARG_SETVERTEXSHADERCONSTB data = {}; + data.Register = 15; + data.Count = 1; + BOOL value = spriteMode && Config::Settings::SpriteTexCoord::ROUND == Config::spriteTexCoord.get(); + pfnSetVertexShaderConstB(&data, &value); + } + } + void DeviceState::setStreamSource(const D3DDDIARG_SETSTREAMSOURCE& streamSource) { if (0 == memcmp(&streamSource, &m_current.streamSource, sizeof(streamSource))) @@ -673,6 +784,27 @@ namespace D3dDdi m_device.flushPrimitives(); m_device.getOrigVtable().pfnSetTexture(m_device, stage, texture); m_current.textures[stage] = texture; + + if (0 == stage && texture) + { + auto resource = (texture == m_app.textures[stage]) ? getTextureResource(stage) : m_device.getResource(texture); + if (resource) + { + D3DDDIARG_SETVERTEXSHADERCONST data = {}; + data.Register = 253; + data.Count = 1; + + auto& si = resource->getFixedDesc().pSurfList[0]; + ShaderConstF reg = { static_cast(si.Width), static_cast(si.Height), + m_vertexShaderConst[253][2], m_vertexShaderConst[253][3] }; + + if (0 != memcmp(®, &m_vertexShaderConst[data.Register], sizeof(reg))) + { + pfnSetVertexShaderConst(&data, ®); + } + } + } + LOG_DS << stage << " " << texture; return true; } @@ -750,7 +882,8 @@ namespace D3dDdi void DeviceState::updateConfig() { - m_changedStates |= CS_RENDER_TARGET | CS_TEXTURE_STAGE; + m_changedStates |= CS_RENDER_STATE | CS_RENDER_TARGET | CS_TEXTURE_STAGE; + m_changedRenderStates.set(D3DDDIRS_MULTISAMPLEANTIALIAS); for (UINT i = 0; i < m_changedTextureStageStates.size(); ++i) { m_changedTextureStageStates[i].set(D3DDDITSS_MINFILTER); @@ -758,6 +891,8 @@ namespace D3dDdi m_changedTextureStageStates[i].set(D3DDDITSS_MIPFILTER); m_changedTextureStageStates[i].set(D3DDDITSS_MAXANISOTROPY); } + m_changedTextureStageStates[0].set(D3DDDITSS_ADDRESSU); + m_changedTextureStageStates[0].set(D3DDDITSS_ADDRESSV); m_maxChangedTextureStage = m_changedTextureStageStates.size() - 1; } @@ -813,7 +948,8 @@ namespace D3dDdi { setPixelShader(m_app.pixelShader); setVertexShaderDecl(m_app.vertexShaderDecl); - if (m_swVertexShaderDecls.find(m_current.vertexShaderDecl) != m_swVertexShaderDecls.end()) + auto it = m_vertexShaderDecls.find(m_app.vertexShaderDecl); + if (it != m_vertexShaderDecls.end() && it->second.isTransformed) { setVertexShaderFunc(m_vsVertexFixup.get()); } @@ -885,7 +1021,7 @@ namespace D3dDdi m_changedTextureStageStates[stage].forEach([&](UINT stateIndex) { const auto state = static_cast(stateIndex); - setTextureStageState({ stage, state, mapTssValue(state, m_app.textureStageState[stage][state]) }); + setTextureStageState({ stage, state, mapTssValue(stage, state, m_app.textureStageState[stage][state]) }); }); m_changedTextureStageStates[stage].reset(); } @@ -894,16 +1030,18 @@ namespace D3dDdi void DeviceState::updateVertexFixupConstants() { D3DDDIARG_SETVERTEXSHADERCONST data = {}; - data.Register = 254; - data.Count = 2; + data.Register = 253; + data.Count = 3; + const float stc = static_cast(Config::spriteTexCoord.getParam()) / 100; const float apc = Config::alternatePixelCenter.get(); const auto& vp = m_app.viewport; const auto& zr = m_current.zRange; const float sx = static_cast(m_current.viewport.Width) / m_app.viewport.Width; const float sy = static_cast(m_current.viewport.Height) / m_app.viewport.Height; - ShaderConstF registers[2] = { + ShaderConstF registers[3] = { + { m_vertexShaderConst[253][0], m_vertexShaderConst[253][1], stc, stc }, { 0.5f + apc - 0.5f / sx - vp.X - vp.Width / 2, 0.5f + apc - 0.5f / sy - vp.Y - vp.Height / 2, -zr.MinZ, 0.0f }, { 2.0f / vp.Width, -2.0f / vp.Height, 1.0f / (zr.MaxZ - zr.MinZ), 1.0f } }; diff --git a/DDrawCompat/D3dDdi/DeviceState.h b/DDrawCompat/D3dDdi/DeviceState.h index c3fc2ca..5b5b5c7 100644 --- a/DDrawCompat/D3dDdi/DeviceState.h +++ b/DDrawCompat/D3dDdi/DeviceState.h @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -19,6 +19,7 @@ const UINT D3DTEXF_ANISOTROPIC = 3; namespace D3dDdi { class Device; + class Resource; class DeviceState { @@ -27,6 +28,24 @@ namespace D3dDdi typedef std::array ShaderConstF; typedef std::array ShaderConstI; + struct State + { + D3DDDIARG_SETDEPTHSTENCIL depthStencil; + HANDLE pixelShader; + std::array renderState; + D3DDDIARG_SETRENDERTARGET renderTarget; + D3DDDIARG_SETSTREAMSOURCE streamSource; + D3DDDIARG_SETSTREAMSOURCEUM streamSourceUm; + const void* streamSourceUmBuffer; + std::array textures; + std::array, 8> textureStageState; + HANDLE vertexShaderDecl; + HANDLE vertexShaderFunc; + D3DDDIARG_VIEWPORTINFO viewport; + D3DDDIARG_WINFO wInfo; + D3DDDIARG_ZRANGE zRange; + }; + class TempPixelShaderConst { public: @@ -43,19 +62,26 @@ namespace D3dDdi public: TempStateLock(DeviceState& state) : m_state(state) - , m_prevChangedStates(state.m_changedStates) { - state.m_changedStates = 0; + state.m_isLocked = true; } ~TempStateLock() { - m_state.m_changedStates = m_prevChangedStates; + m_state.m_isLocked = false; } private: DeviceState& m_state; - UINT m_prevChangedStates; + }; + + struct VertexDecl + { + std::vector elements; + std::array texCoordOffset; + std::array texCoordType; + UINT textureStageCount; + bool isTransformed; }; DeviceState(Device& device); @@ -84,6 +110,7 @@ namespace D3dDdi HRESULT pfnSetZRange(const D3DDDIARG_ZRANGE* data); HRESULT pfnUpdateWInfo(const D3DDDIARG_WINFO* data); + void setSpriteMode(bool spriteMode); void setTempDepthStencil(const D3DDDIARG_SETDEPTHSTENCIL& depthStencil); void setTempPixelShader(HANDLE shader); void setTempRenderState(const D3DDDIARG_RENDERSTATE& renderState); @@ -97,10 +124,16 @@ namespace D3dDdi void setTempWInfo(const D3DDDIARG_WINFO& wInfo); void setTempZRange(const D3DDDIARG_ZRANGE& zRange); + void disableTextureClamp(UINT stage); void flush(); + const State& getAppState() const { return m_app; } + Resource* getTextureResource(UINT stage); + const VertexDecl& getVertexDecl() const; HANDLE getVertexFixupDecl() const { return m_vsVertexFixup.get(); } - void onDestroyResource(HANDLE resource); + bool isLocked() const { return m_isLocked; } + void onDestroyResource(D3dDdi::Resource* resource, HANDLE resourceHandle); void updateConfig(); + void updateStreamSource(); private: friend class ScopedDeviceState; @@ -114,24 +147,6 @@ namespace D3dDdi CS_TEXTURE_STAGE = 1 << 4 }; - struct State - { - D3DDDIARG_SETDEPTHSTENCIL depthStencil; - HANDLE pixelShader; - std::array renderState; - D3DDDIARG_SETRENDERTARGET renderTarget; - D3DDDIARG_SETSTREAMSOURCE streamSource; - D3DDDIARG_SETSTREAMSOURCEUM streamSourceUm; - const void* streamSourceUmBuffer; - std::array textures; - std::array, 8> textureStageState; - HANDLE vertexShaderDecl; - HANDLE vertexShaderFunc; - D3DDDIARG_VIEWPORTINFO viewport; - D3DDDIARG_WINFO wInfo; - D3DDDIARG_ZRANGE zRange; - }; - template std::unique_ptr createVertexShader(const BYTE(&code)[N]) { @@ -142,6 +157,9 @@ namespace D3dDdi HRESULT deleteShader(HANDLE shader, HANDLE State::* shaderMember, HRESULT(APIENTRY* origDeleteShaderFunc)(HANDLE, HANDLE)); + UINT mapRsValue(D3DDDIRENDERSTATETYPE state, UINT value); + UINT mapTssValue(UINT stage, D3DDDITEXTURESTAGESTATETYPE state, UINT value); + template void removeResource(HANDLE resource, Data State::* data, HANDLE Data::* resourceMember, HRESULT(DeviceState::* pfnSetResourceFunc)(const Data*)); @@ -173,7 +191,6 @@ namespace D3dDdi void updateRenderStates(); void updateRenderTarget(); void updateShaders(); - void updateStreamSource(); void updateTextureColorKey(UINT stage); void updateTextureStages(); void updateVertexFixupConstants(); @@ -187,11 +204,15 @@ namespace D3dDdi std::array m_vertexShaderConst; std::array m_vertexShaderConstB; std::array m_vertexShaderConstI; - std::set m_swVertexShaderDecls; + std::map m_vertexShaderDecls; UINT m_changedStates; UINT m_maxChangedTextureStage; + UINT m_usedTextureStages; BitSet m_changedRenderStates; std::array, 8> m_changedTextureStageStates; std::unique_ptr m_vsVertexFixup; + std::array m_textureResource; + bool m_isLocked; + bool m_spriteMode; }; } diff --git a/DDrawCompat/D3dDdi/DrawPrimitive.cpp b/DDrawCompat/D3dDdi/DrawPrimitive.cpp index 2eef343..7c14c3e 100644 --- a/DDrawCompat/D3dDdi/DrawPrimitive.cpp +++ b/DDrawCompat/D3dDdi/DrawPrimitive.cpp @@ -453,13 +453,35 @@ namespace D3dDdi HRESULT DrawPrimitive::draw(D3DDDIARG_DRAWPRIMITIVE data, const UINT* flagBuffer) { - m_device.getState().flush(); + auto& state = m_device.getState(); + if (!state.isLocked()) + { + state.updateStreamSource(); + } + + auto vertexCount = getVertexCount(data.PrimitiveType, data.PrimitiveCount); + if (!state.isLocked()) + { + if (m_streamSource.vertices && data.PrimitiveType >= D3DPT_TRIANGLELIST) + { + bool spriteMode = isSprite(data.VStart, 0, 1, 2); + state.setSpriteMode(spriteMode); + if (spriteMode) + { + setTextureClampMode(data.VStart, nullptr, vertexCount); + } + } + else + { + state.setSpriteMode(false); + } + state.flush(); + } if (0 == m_batched.primitiveCount || flagBuffer || !appendPrimitives(data.PrimitiveType, data.VStart, data.PrimitiveCount, nullptr, 0, 0)) { flushPrimitives(); - auto vertexCount = getVertexCount(data.PrimitiveType, data.PrimitiveCount); if (m_streamSource.vertices) { appendVertices(data.VStart, vertexCount); @@ -486,19 +508,41 @@ namespace D3dDdi HRESULT DrawPrimitive::drawIndexed( D3DDDIARG_DRAWINDEXEDPRIMITIVE2 data, const UINT16* indices, const UINT* flagBuffer) { - m_device.getState().flush(); + auto& state = m_device.getState(); + if (!state.isLocked()) + { + state.updateStreamSource(); + } auto indexCount = getVertexCount(data.PrimitiveType, data.PrimitiveCount); + auto vStart = data.BaseVertexOffset / static_cast(m_streamSource.stride); + if (!state.isLocked()) + { + if (m_streamSource.vertices && data.PrimitiveType >= D3DPT_TRIANGLELIST) + { + bool spriteMode = isSprite(vStart, indices[0], indices[1], indices[2]); + state.setSpriteMode(spriteMode); + if (spriteMode) + { + setTextureClampMode(vStart, indices, indexCount); + } + } + else + { + state.setSpriteMode(false); + } + state.flush(); + } + auto [min, max] = std::minmax_element(indices, indices + indexCount); data.MinIndex = *min; data.NumVertices = *max - *min + 1; if (0 == m_batched.primitiveCount || flagBuffer || - !appendPrimitives(data.PrimitiveType, data.BaseVertexOffset / static_cast(m_streamSource.stride), - data.PrimitiveCount, indices, *min, *max)) + !appendPrimitives(data.PrimitiveType, vStart, data.PrimitiveCount, indices, *min, *max)) { flushPrimitives(); - m_batched.baseVertexIndex = data.BaseVertexOffset / static_cast(m_streamSource.stride); + m_batched.baseVertexIndex = vStart; if (m_streamSource.vertices) { appendIndexedVerticesWithoutRebase(indices, indexCount, m_batched.baseVertexIndex, *min, *max); @@ -605,6 +649,15 @@ namespace D3dDdi return m_batched.vertices.size() / m_streamSource.stride; } + bool DrawPrimitive::isSprite(INT baseVertexIndex, UINT16 index0, UINT16 index1, UINT16 index2) + { + auto v = m_streamSource.vertices + baseVertexIndex * m_streamSource.stride; + auto v0 = reinterpret_cast(v + index0 * m_streamSource.stride); + auto v1 = reinterpret_cast(v + index1 * m_streamSource.stride); + auto v2 = reinterpret_cast(v + index2 * m_streamSource.stride); + return v0->sz == v1->sz && v0->sz == v2->sz; + } + INT DrawPrimitive::loadIndices(const void* indices, UINT count) { INT startIndex = m_indexBuffer.load(indices, count); @@ -748,4 +801,53 @@ namespace D3dDdi } return result; } + + void DrawPrimitive::setTextureClampMode(INT baseVertexIndex, const UINT16* indices, UINT count) + { + if (Config::Settings::SpriteTexCoord::CLAMP != Config::spriteTexCoord.get()) + { + return; + } + + auto& state = m_device.getState(); + auto& appState = state.getAppState(); + auto& decl = state.getVertexDecl(); + auto vertices = m_streamSource.vertices + baseVertexIndex * m_streamSource.stride; + + for (UINT stage = 0; stage < decl.textureStageCount; ++stage) + { + const UINT D3DDECLTYPE_FLOAT2 = 1; + if (!appState.textures[stage] || + D3DDECLTYPE_FLOAT2 != decl.texCoordType[stage] || + D3DTADDRESS_CLAMP == appState.textureStageState[stage][D3DDDITSS_ADDRESSU] && + D3DTADDRESS_CLAMP == appState.textureStageState[stage][D3DDDITSS_ADDRESSV]) + { + continue; + } + + auto resource = state.getTextureResource(stage); + if (!resource || !resource->isClampable()) + { + continue; + } + + const float texelWidth = 1 / static_cast(resource->getFixedDesc().pSurfList[0].Width); + const float texelHeight = 1 / static_cast(resource->getFixedDesc().pSurfList[0].Height); + const float minU = -texelWidth; + const float maxU = 1 + texelWidth; + const float minV = -texelHeight; + const float maxV = 1 + texelHeight; + + for (UINT i = 0; i < count; ++i) + { + auto vertex = vertices + (indices ? indices[i] : i) * m_streamSource.stride; + const float* texCoord = reinterpret_cast(vertex + decl.texCoordOffset[stage]); + if (texCoord[0] < minU || texCoord[0] > maxU || texCoord[1] < minV || texCoord[1] > maxV) + { + state.disableTextureClamp(stage); + break; + } + } + } + } } diff --git a/DDrawCompat/D3dDdi/DrawPrimitive.h b/DDrawCompat/D3dDdi/DrawPrimitive.h index 29029e6..3036ca8 100644 --- a/DDrawCompat/D3dDdi/DrawPrimitive.h +++ b/DDrawCompat/D3dDdi/DrawPrimitive.h @@ -70,6 +70,7 @@ namespace D3dDdi void convertToTriangleList(); HRESULT flush(const UINT* flagBuffer); HRESULT flushIndexed(const UINT* flagBuffer); + bool isSprite(INT baseVertexIndex, UINT16 index0, UINT16 index1, UINT16 index2); INT loadIndices(const void* indices, UINT count); INT loadVertices(UINT count); UINT getBatchedVertexCount() const; @@ -77,6 +78,7 @@ namespace D3dDdi void repeatLastBatchedVertex(); HRESULT setSysMemStreamSource(const BYTE* vertices, UINT stride); + void setTextureClampMode(INT baseVertexIndex, const UINT16* indices, UINT count); Device& m_device; const D3DDDI_DEVICEFUNCS& m_origVtable; diff --git a/DDrawCompat/D3dDdi/Resource.cpp b/DDrawCompat/D3dDdi/Resource.cpp index fa4e517..1f6f0a3 100644 --- a/DDrawCompat/D3dDdi/Resource.cpp +++ b/DDrawCompat/D3dDdi/Resource.cpp @@ -117,6 +117,7 @@ namespace D3dDdi , m_scaledSize{} , m_isOversized(false) , m_isSurfaceRepoResource(SurfaceRepository::inCreateSurface()) + , m_isClampable(true) { if (m_origData.Flags.VertexBuffer && m_origData.Flags.MightDrawFromLocked && @@ -440,6 +441,11 @@ namespace D3dDdi #endif } + void Resource::disableClamp() + { + m_isClampable = false; + } + bool Resource::downscale(Resource*& rt, LONG& srcWidth, LONG& srcHeight, LONG dstWidth, LONG dstHeight) { LONG newSrcWidth = (srcWidth + 1) / 2; diff --git a/DDrawCompat/D3dDdi/Resource.h b/DDrawCompat/D3dDdi/Resource.h index 91e3d6b..2d59e1e 100644 --- a/DDrawCompat/D3dDdi/Resource.h +++ b/DDrawCompat/D3dDdi/Resource.h @@ -29,9 +29,11 @@ namespace D3dDdi const Resource* getCustomResource() { return m_msaaSurface.resource ? m_msaaSurface.resource : m_msaaResolvedSurface.resource; } const D3DDDIARG_CREATERESOURCE2& getFixedDesc() const { return m_fixedData; } const D3DDDIARG_CREATERESOURCE2& getOrigDesc() const { return m_origData; } + bool isClampable() const { return m_isClampable; } HRESULT blt(D3DDDIARG_BLT data); HRESULT colorFill(D3DDDIARG_COLORFILL data); + void disableClamp(); void* getLockPtr(UINT subResourceIndex); HRESULT lock(D3DDDIARG_LOCK& data); void onDestroyResource(HANDLE resource); @@ -115,5 +117,6 @@ namespace D3dDdi SIZE m_scaledSize; bool m_isOversized; bool m_isSurfaceRepoResource; + bool m_isClampable; }; } diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index b0f09b4..083e3ad 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -115,6 +115,7 @@ 2.0 $(IntDir)%(RelativeDir)%(Filename).h Pixel + false @@ -226,6 +227,7 @@ + @@ -350,6 +352,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 22db839..2840f27 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -528,6 +528,9 @@ Header Files\Config\Settings + + Header Files\Config\Settings + @@ -833,6 +836,9 @@ Source Files\Input + + Source Files\Config\Settings + diff --git a/DDrawCompat/Overlay/ConfigWindow.cpp b/DDrawCompat/Overlay/ConfigWindow.cpp index 4095ea9..14735aa 100644 --- a/DDrawCompat/Overlay/ConfigWindow.cpp +++ b/DDrawCompat/Overlay/ConfigWindow.cpp @@ -8,7 +8,7 @@ namespace Overlay { ConfigWindow::ConfigWindow() - : Window(nullptr, { 0, 0, SettingControl::TOTAL_WIDTH, 200 }, Config::configHotKey.get()) + : Window(nullptr, { 0, 0, SettingControl::TOTAL_WIDTH, 300 }, Config::configHotKey.get()) , m_focus(nullptr) { addControl(Config::alternatePixelCenter); @@ -16,6 +16,7 @@ namespace Overlay addControl(Config::displayFilter); addControl(Config::renderColorDepth); addControl(Config::resolutionScale); + addControl(Config::spriteTexCoord); addControl(Config::textureFilter); } diff --git a/DDrawCompat/Overlay/SettingControl.cpp b/DDrawCompat/Overlay/SettingControl.cpp index 29699fa..573a084 100644 --- a/DDrawCompat/Overlay/SettingControl.cpp +++ b/DDrawCompat/Overlay/SettingControl.cpp @@ -78,6 +78,7 @@ namespace Overlay if (&Config::antialiasing == &m_setting || &Config::renderColorDepth == &m_setting || &Config::resolutionScale == &m_setting || + &Config::spriteTexCoord == &m_setting || &Config::textureFilter == &m_setting) { D3dDdi::Device::updateAllConfig(); diff --git a/DDrawCompat/Shaders/VertexFixup.hlsl b/DDrawCompat/Shaders/VertexFixup.hlsl index af1c6ce..1543aaa 100644 --- a/DDrawCompat/Shaders/VertexFixup.hlsl +++ b/DDrawCompat/Shaders/VertexFixup.hlsl @@ -1,3 +1,5 @@ +bool g_useTexCoordAdj : register(b15); +float4 g_texCoordAdj : register(c253); float4 g_offset : register(c254); float4 g_multiplier : register(c255); @@ -19,5 +21,9 @@ VS main(const VS i) o.pos.xyz *= w; o.pos.w = w; o.fog = i.color[1].a; + if (g_useTexCoordAdj) + { + o.tex[0].xy = round(o.tex[0].xy * g_texCoordAdj.xy + g_texCoordAdj.zw) / g_texCoordAdj.xy; + } return o; }