mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
261 lines
10 KiB
C++
261 lines
10 KiB
C++
#include <Common/Log.h>
|
|
#include <D3dDdi/Adapter.h>
|
|
#include <D3dDdi/Device.h>
|
|
#include <D3dDdi/Resource.h>
|
|
#include <D3dDdi/ShaderBlitter.h>
|
|
#include <D3dDdi/SurfaceRepository.h>
|
|
#include <Shaders/DrawCursor.h>
|
|
#include <Shaders/GenBilinear.h>
|
|
#include <Shaders/PaletteLookup.h>
|
|
#include <Shaders/TextureSampler.h>
|
|
|
|
#define CONCAT_(a, b) a##b
|
|
#define CONCAT(a, b) CONCAT_(a, b)
|
|
#define SCOPED_STATE(state, ...) DeviceState::Scoped##state CONCAT(scopedState, __LINE__)(m_device.getState(), __VA_ARGS__)
|
|
|
|
namespace D3dDdi
|
|
{
|
|
ShaderBlitter::ShaderBlitter(Device& device)
|
|
: m_device(device)
|
|
, m_psDrawCursor(createPixelShader(g_psDrawCursor))
|
|
, m_psGenBilinear(createPixelShader(g_psGenBilinear))
|
|
, m_psPaletteLookup(createPixelShader(g_psPaletteLookup))
|
|
, m_psTextureSampler(createPixelShader(g_psTextureSampler))
|
|
, m_vertexShaderDecl(createVertexShaderDecl())
|
|
{
|
|
}
|
|
|
|
void ShaderBlitter::blt(const Resource& dstResource, UINT dstSubResourceIndex, const RECT& dstRect,
|
|
const Resource& srcResource, UINT srcSubResourceIndex, const RECT& srcRect,
|
|
HANDLE pixelShader, UINT filter, const UINT* srcColorKey)
|
|
{
|
|
LOG_FUNC("ShaderBlitter::blt", static_cast<HANDLE>(dstResource), dstSubResourceIndex, dstRect,
|
|
static_cast<HANDLE>(srcResource), srcRect, srcSubResourceIndex, pixelShader, filter, srcColorKey);
|
|
|
|
if (!m_vertexShaderDecl || !pixelShader)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto& srcSurface = srcResource.getFixedDesc().pSurfList[srcSubResourceIndex];
|
|
const auto& dstSurface = dstResource.getFixedDesc().pSurfList[dstSubResourceIndex];
|
|
|
|
auto& state = m_device.getState();
|
|
state.setTempRenderState({ D3DDDIRS_SCENECAPTURE, TRUE });
|
|
state.setTempVertexShaderDecl(m_vertexShaderDecl.get());
|
|
state.setTempPixelShader(pixelShader);
|
|
state.setTempRenderTarget({ 0, dstResource, dstSubResourceIndex });
|
|
state.setTempDepthStencil({ nullptr });
|
|
state.setTempViewport({ 0, 0, dstSurface.Width, dstSurface.Height });
|
|
state.setTempZRange({ 0, 1 });
|
|
|
|
state.setTempRenderState({ D3DDDIRS_ZENABLE, D3DZB_FALSE });
|
|
state.setTempRenderState({ D3DDDIRS_FILLMODE, D3DFILL_SOLID });
|
|
state.setTempRenderState({ D3DDDIRS_ZWRITEENABLE, FALSE });
|
|
state.setTempRenderState({ D3DDDIRS_ALPHATESTENABLE, FALSE });
|
|
state.setTempRenderState({ D3DDDIRS_CULLMODE, D3DCULL_NONE });
|
|
state.setTempRenderState({ D3DDDIRS_DITHERENABLE, FALSE });
|
|
state.setTempRenderState({ D3DDDIRS_ALPHABLENDENABLE, FALSE });
|
|
state.setTempRenderState({ D3DDDIRS_FOGENABLE, FALSE });
|
|
state.setTempRenderState({ D3DDDIRS_COLORKEYENABLE, nullptr != srcColorKey });
|
|
state.setTempRenderState({ D3DDDIRS_STENCILENABLE, FALSE });
|
|
state.setTempRenderState({ D3DDDIRS_CLIPPING, FALSE });
|
|
state.setTempRenderState({ D3DDDIRS_CLIPPLANEENABLE, 0 });
|
|
state.setTempRenderState({ D3DDDIRS_MULTISAMPLEANTIALIAS, FALSE });
|
|
state.setTempRenderState({ D3DDDIRS_COLORWRITEENABLE, 0xF });
|
|
state.setTempRenderState({ D3DDDIRS_SRGBWRITEENABLE, D3DTEXF_LINEAR == filter });
|
|
|
|
setTempTextureStage(0, srcResource, filter, srcColorKey);
|
|
|
|
struct Vertex
|
|
{
|
|
float x;
|
|
float y;
|
|
float z;
|
|
float rhw;
|
|
float tu;
|
|
float tv;
|
|
};
|
|
|
|
const float srcWidth = static_cast<float>(srcSurface.Width);
|
|
const float srcHeight = static_cast<float>(srcSurface.Height);
|
|
|
|
Vertex vertices[4] = {
|
|
{ dstRect.left - 0.5f, dstRect.top - 0.5f, 0, 1, srcRect.left / srcWidth, srcRect.top / srcHeight },
|
|
{ dstRect.right - 0.5f, dstRect.top - 0.5f, 0, 1, srcRect.right / srcWidth, srcRect.top / srcHeight },
|
|
{ dstRect.left - 0.5f, dstRect.bottom - 0.5f, 0, 1, srcRect.left / srcWidth, srcRect.bottom / srcHeight },
|
|
{ dstRect.right - 0.5f, dstRect.bottom - 0.5f, 0, 1, srcRect.right / srcWidth, srcRect.bottom / srcHeight }
|
|
};
|
|
|
|
state.setTempStreamSourceUm({ 0, sizeof(Vertex) }, vertices);
|
|
|
|
DeviceState::TempStateLock lock(state);
|
|
|
|
D3DDDIARG_DRAWPRIMITIVE dp = {};
|
|
dp.PrimitiveType = D3DPT_TRIANGLESTRIP;
|
|
dp.VStart = 0;
|
|
dp.PrimitiveCount = 2;
|
|
m_device.pfnDrawPrimitive(&dp, nullptr);
|
|
m_device.flushPrimitives();
|
|
}
|
|
|
|
std::unique_ptr<void, ResourceDeleter> ShaderBlitter::createPixelShader(const BYTE* code, UINT size)
|
|
{
|
|
D3DDDIARG_CREATEPIXELSHADER data = {};
|
|
data.CodeSize = size;
|
|
if (FAILED(m_device.getOrigVtable().pfnCreatePixelShader(m_device, &data, reinterpret_cast<const UINT*>(code))))
|
|
{
|
|
return nullptr;
|
|
}
|
|
return { data.ShaderHandle, ResourceDeleter(m_device, m_device.getOrigVtable().pfnDeletePixelShader) };
|
|
}
|
|
|
|
std::unique_ptr<void, ResourceDeleter> ShaderBlitter::createVertexShaderDecl()
|
|
{
|
|
const UINT D3DDECLTYPE_FLOAT2 = 1;
|
|
const UINT D3DDECLTYPE_FLOAT4 = 3;
|
|
const UINT D3DDECLMETHOD_DEFAULT = 0;
|
|
const UINT D3DDECLUSAGE_TEXCOORD = 5;
|
|
const UINT D3DDECLUSAGE_POSITIONT = 9;
|
|
|
|
const D3DDDIVERTEXELEMENT vertexElements[] = {
|
|
{ 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 },
|
|
{ 0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }
|
|
};
|
|
|
|
D3DDDIARG_CREATEVERTEXSHADERDECL data = {};
|
|
data.NumVertexElements = sizeof(vertexElements) / sizeof(vertexElements[0]);
|
|
|
|
if (FAILED(m_device.getOrigVtable().pfnCreateVertexShaderDecl(m_device, &data, vertexElements)))
|
|
{
|
|
return nullptr;
|
|
}
|
|
return { data.ShaderHandle, ResourceDeleter(m_device, m_device.getOrigVtable().pfnDeleteVertexShaderDecl) };
|
|
}
|
|
|
|
void ShaderBlitter::cursorBlt(const Resource& dstResource, UINT dstSubResourceIndex, HCURSOR cursor, POINT pt)
|
|
{
|
|
LOG_FUNC("ShaderBlitter::cursorBlt", static_cast<HANDLE>(dstResource), dstSubResourceIndex, cursor, pt);
|
|
|
|
auto& repo = SurfaceRepository::get(m_device.getAdapter());
|
|
auto cur = repo.getCursor(cursor);
|
|
auto xorTexture = repo.getLogicalXorTexture();
|
|
|
|
pt.x -= cur.hotspot.x;
|
|
pt.y -= cur.hotspot.y;
|
|
RECT dstRect = { pt.x, pt.y, pt.x + cur.size.cx, pt.y + cur.size.cy };
|
|
|
|
auto& dstDesc = dstResource.getFixedDesc().pSurfList[dstSubResourceIndex];
|
|
RECT clippedDstRect = {};
|
|
clippedDstRect.right = dstDesc.Width;
|
|
clippedDstRect.bottom = dstDesc.Height;
|
|
IntersectRect(&clippedDstRect, &clippedDstRect, &dstRect);
|
|
|
|
if (!cur.maskTexture || !cur.colorTexture || !cur.tempTexture || !xorTexture || IsRectEmpty(&clippedDstRect))
|
|
{
|
|
return;
|
|
}
|
|
|
|
RECT clippedSrcRect = clippedDstRect;
|
|
OffsetRect(&clippedSrcRect, -dstRect.left, -dstRect.top);
|
|
|
|
D3DDDIARG_BLT data = {};
|
|
data.hSrcResource = dstResource;
|
|
data.SrcSubResourceIndex = dstSubResourceIndex;
|
|
data.SrcRect = clippedDstRect;
|
|
data.hDstResource = *cur.tempTexture;
|
|
data.DstRect = clippedSrcRect;
|
|
m_device.getOrigVtable().pfnBlt(m_device, &data);
|
|
|
|
setTempTextureStage(1, *cur.maskTexture, D3DTEXF_POINT);
|
|
setTempTextureStage(2, *cur.colorTexture, D3DTEXF_POINT);
|
|
setTempTextureStage(3, *xorTexture, D3DTEXF_POINT);
|
|
blt(dstResource, dstSubResourceIndex, clippedDstRect, *cur.tempTexture, 0, clippedSrcRect,
|
|
m_psDrawCursor.get(), D3DTEXF_POINT);
|
|
}
|
|
|
|
void ShaderBlitter::genBilinearBlt(const Resource& dstResource, UINT dstSubResourceIndex, const RECT& dstRect,
|
|
const Resource& srcResource, const RECT& srcRect, UINT blurPercent)
|
|
{
|
|
if (100 == blurPercent)
|
|
{
|
|
blt(dstResource, dstSubResourceIndex, dstRect, srcResource, 0, srcRect,
|
|
m_psTextureSampler.get(), D3DTEXF_LINEAR);
|
|
return;
|
|
}
|
|
|
|
const auto& srcDesc = srcResource.getFixedDesc().pSurfList[0];
|
|
float scaleX = static_cast<float>(dstRect.right - dstRect.left) / (srcRect.right - srcRect.left);
|
|
float scaleY = static_cast<float>(dstRect.bottom - dstRect.top) / (srcRect.bottom - srcRect.top);
|
|
|
|
const float blur = blurPercent / 100.0f;
|
|
scaleX = 1 / ((1 - blur) / scaleX + blur);
|
|
scaleY = 1 / ((1 - blur) / scaleY + blur);
|
|
|
|
const std::array<DeviceState::ShaderConstF, 2> registers{ {
|
|
{ static_cast<float>(srcDesc.Width), static_cast<float>(srcDesc.Height), 0.0f, 0.0f },
|
|
{ scaleX, scaleY, 0.0f, 0.0f }
|
|
} };
|
|
|
|
DeviceState::TempPixelShaderConst tempPsConst(m_device.getState(), { 0, registers.size() }, registers.data());
|
|
blt(dstResource, dstSubResourceIndex, dstRect, srcResource, 0, srcRect, m_psGenBilinear.get(), D3DTEXF_LINEAR);
|
|
}
|
|
|
|
void ShaderBlitter::palettizedBlt(const Resource& dstResource, UINT dstSubResourceIndex,
|
|
const Resource& srcResource, RGBQUAD palette[256])
|
|
{
|
|
LOG_FUNC("ShaderBlitter::palettizedBlt", static_cast<HANDLE>(dstResource), dstSubResourceIndex,
|
|
static_cast<HANDLE>(srcResource), Compat::array(reinterpret_cast<void**>(palette), 256));
|
|
|
|
auto paletteTexture(SurfaceRepository::get(m_device.getAdapter()).getPaletteTexture());
|
|
if (!paletteTexture)
|
|
{
|
|
return;
|
|
}
|
|
|
|
D3DDDIARG_LOCK lock = {};
|
|
lock.hResource = *paletteTexture;
|
|
lock.Flags.Discard = 1;
|
|
m_device.getOrigVtable().pfnLock(m_device, &lock);
|
|
if (!lock.pSurfData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
memcpy(lock.pSurfData, palette, 256 * sizeof(RGBQUAD));
|
|
|
|
D3DDDIARG_UNLOCK unlock = {};
|
|
unlock.hResource = *paletteTexture;
|
|
m_device.getOrigVtable().pfnUnlock(m_device, &unlock);
|
|
|
|
const auto& dstSurface = dstResource.getFixedDesc().pSurfList[dstSubResourceIndex];
|
|
const auto& srcSurface = srcResource.getFixedDesc().pSurfList[0];
|
|
const RECT dstRect = { 0, 0, static_cast<LONG>(dstSurface.Width), static_cast<LONG>(dstSurface.Height) };
|
|
const RECT srcRect = { 0, 0, static_cast<LONG>(srcSurface.Width), static_cast<LONG>(srcSurface.Height) };
|
|
|
|
setTempTextureStage(1, *paletteTexture, D3DTEXF_POINT);
|
|
blt(dstResource, dstSubResourceIndex, dstRect, srcResource, 0, srcRect, m_psPaletteLookup.get(), D3DTEXF_POINT);
|
|
}
|
|
|
|
void ShaderBlitter::setTempTextureStage(UINT stage, HANDLE texture, UINT filter, const UINT* srcColorKey)
|
|
{
|
|
auto& state = m_device.getState();
|
|
state.setTempTexture(stage, texture, srcColorKey);
|
|
state.setTempTextureStageState({ stage, D3DDDITSS_ADDRESSU, D3DTADDRESS_CLAMP });
|
|
state.setTempTextureStageState({ stage, D3DDDITSS_ADDRESSV, D3DTADDRESS_CLAMP });
|
|
state.setTempTextureStageState({ stage, D3DDDITSS_MAGFILTER, filter });
|
|
state.setTempTextureStageState({ stage, D3DDDITSS_MINFILTER, filter });
|
|
state.setTempTextureStageState({ stage, D3DDDITSS_MIPFILTER, D3DTEXF_NONE });
|
|
state.setTempTextureStageState({ stage, D3DDDITSS_SRGBTEXTURE, D3DTEXF_LINEAR == filter });
|
|
state.setTempRenderState({ static_cast<D3DDDIRENDERSTATETYPE>(D3DDDIRS_WRAP0 + stage), 0 });
|
|
}
|
|
|
|
void ShaderBlitter::textureBlt(const Resource& dstResource, UINT dstSubResourceIndex, const RECT& dstRect,
|
|
const Resource& srcResource, UINT srcSubResourceIndex, const RECT& srcRect,
|
|
UINT filter, const UINT* srcColorKey)
|
|
{
|
|
blt(dstResource, dstSubResourceIndex, dstRect, srcResource, srcSubResourceIndex, srcRect,
|
|
m_psTextureSampler.get(), filter, srcColorKey);
|
|
}
|
|
}
|