dx9: mipmaps. load/dump textures. vmu/crosshair display.

upload all texture mipmaps to gpu
dump textures. load custom textures
vmu and lightgun xhair display
fix dx9 half-pixel offset
fix 565_32 unpacker bug
This commit is contained in:
flyinghead 2021-04-16 18:30:47 +02:00
parent c889bfa6d4
commit abc8b18b78
13 changed files with 480 additions and 155 deletions

View File

@ -867,6 +867,9 @@ endif()
if(WIN32)
target_sources(${PROJECT_NAME} PRIVATE
core/rend/dx9/comptr.h
core/rend/dx9/d3d_overlay.h
core/rend/dx9/d3d_overlay.cpp
core/rend/dx9/d3d_renderer.h
core/rend/dx9/d3d_renderer.cpp
core/rend/dx9/d3d_shaders.h

View File

@ -182,50 +182,90 @@ void CustomTexture::DumpTexture(u32 hash, int w, int h, TextureType textype, voi
for (int y = 0; y < h; y++)
{
switch (textype)
if (!config::RendererType.isDirectX())
{
case TextureType::_4444:
for (int x = 0; x < w; x++)
switch (textype)
{
*dst++ = ((*src >> 12) & 0xF) << 4;
*dst++ = ((*src >> 8) & 0xF) << 4;
*dst++ = ((*src >> 4) & 0xF) << 4;
*dst++ = (*src & 0xF) << 4;
src++;
case TextureType::_4444:
for (int x = 0; x < w; x++)
{
*dst++ = ((*src >> 12) & 0xF) << 4;
*dst++ = ((*src >> 8) & 0xF) << 4;
*dst++ = ((*src >> 4) & 0xF) << 4;
*dst++ = (*src & 0xF) << 4;
src++;
}
break;
case TextureType::_565:
for (int x = 0; x < w; x++)
{
*(u32 *)dst = Unpacker565_32<RGBAPacker>::unpack(*src);
dst += 4;
src++;
}
break;
case TextureType::_5551:
for (int x = 0; x < w; x++)
{
*dst++ = ((*src >> 11) & 0x1F) << 3;
*dst++ = ((*src >> 6) & 0x1F) << 3;
*dst++ = ((*src >> 1) & 0x1F) << 3;
*dst++ = (*src & 1) ? 255 : 0;
src++;
}
break;
case TextureType::_8888:
memcpy(dst, src, w * 4);
dst += w * 4;
src += w * 2;
break;
default:
WARN_LOG(RENDERER, "dumpTexture: unsupported picture format %x", (u32)textype);
free(dst_buffer);
return;
}
break;
case TextureType::_565:
for (int x = 0; x < w; x++)
}
else
{
switch (textype)
{
*dst++ = ((*src >> 11) & 0x1F) << 3;
*dst++ = ((*src >> 5) & 0x3F) << 2;
*dst++ = (*src & 0x1F) << 3;
*dst++ = 255;
src++;
case TextureType::_4444:
for (int x = 0; x < w; x++)
{
*(u32 *)dst = Unpacker4444_32<RGBAPacker>::unpack(*src);
dst += 4;
src++;
}
break;
case TextureType::_565:
for (int x = 0; x < w; x++)
{
*(u32 *)dst = Unpacker565_32<RGBAPacker>::unpack(*src);
dst += 4;
src++;
}
break;
case TextureType::_5551:
for (int x = 0; x < w; x++)
{
*(u32 *)dst = Unpacker1555_32<RGBAPacker>::unpack(*src);
dst += 4;
src++;
}
break;
case TextureType::_8888:
for (int x = 0; x < w; x++)
{
*(u32 *)dst = UnpackerRGBA8888_32<RGBAPacker>::unpack(*(u32 *)src);
dst += 4;
src += 2;
}
break;
default:
WARN_LOG(RENDERER, "dumpTexture: unsupported picture format %x", (u32)textype);
free(dst_buffer);
return;
}
break;
case TextureType::_5551:
for (int x = 0; x < w; x++)
{
*dst++ = ((*src >> 11) & 0x1F) << 3;
*dst++ = ((*src >> 6) & 0x1F) << 3;
*dst++ = ((*src >> 1) & 0x1F) << 3;
*dst++ = (*src & 1) ? 255 : 0;
src++;
}
break;
case TextureType::_8888:
for (int x = 0; x < w; x++)
{
*(u32 *)dst = *(u32 *)src;
dst += 4;
src += 2;
}
break;
default:
WARN_LOG(RENDERER, "dumpTexture: unsupported picture format %x", (u32)textype);
free(dst_buffer);
return;
}
}

View File

@ -206,7 +206,7 @@ struct Unpacker565_32 {
return Packer::pack(
(((word >> 11) & 0x1F) << 3) | ((word >> 13) & 7),
(((word >> 5) & 0x3F) << 2) | ((word >> 9) & 3),
(((word >> 0) & 0x1F) << 13) | ((word >> 2) & 7),
(((word >> 0) & 0x1F) << 3) | ((word >> 2) & 7),
0xFF);
}
};

71
core/rend/dx9/comptr.h Normal file
View File

@ -0,0 +1,71 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <utility>
template<typename T>
class ComPtr
{
public:
ComPtr() = default;
ComPtr(const ComPtr& other) : ptr(other.ptr) {
if (ptr != nullptr)
ptr->AddRef();
}
ComPtr(ComPtr&& other) noexcept {
std::swap(ptr, other.ptr);
}
~ComPtr() {
if (ptr != nullptr)
ptr->Release();
}
ComPtr& operator=(const ComPtr& other) {
if (this != &other)
*this = ComPtr(other);
return *this;
}
ComPtr& operator=(ComPtr&& other) noexcept {
std::swap(ptr, other.ptr);
return *this;
}
T* operator->() const noexcept {
return ptr;
}
explicit operator bool() const noexcept {
return ptr != nullptr;
}
operator T*() const noexcept {
return ptr;
}
T*& get() noexcept {
return ptr;
}
void reset(T *ptr = nullptr) {
if (ptr == this->ptr)
return;
std::swap(this->ptr, ptr);
if (ptr != nullptr)
ptr->Release();
}
private:
T *ptr = nullptr;
};

View File

@ -0,0 +1,162 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "d3d_overlay.h"
#include "rend/gui.h"
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
void D3DOverlay::drawQuad(const RECT& rect, D3DCOLOR color)
{
device->SetTextureStageState(0, D3DTSS_CONSTANT, color);
Vertex quad[] {
{ (float)(rect.left), (float)(rect.top), 0.5f, 0.f, 0.f },
{ (float)(rect.left), (float)(rect.bottom), 0.5f, 0.f, 1.f },
{ (float)(rect.right), (float)(rect.top), 0.5f, 1.f, 0.f },
{ (float)(rect.right), (float)(rect.bottom), 0.5f, 1.f, 1.f }
};
device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, quad, sizeof(Vertex));
}
void D3DOverlay::draw(u32 width, u32 height, bool vmu, bool crosshair)
{
setupRenderState(width, height);
if (vmu)
{
float vmu_padding = 8.f * scaling;
float vmu_height = 70.f * scaling;
float vmu_width = 48.f / 32.f * vmu_height;
for (size_t i = 0; i < vmuTextures.size(); i++)
{
ComPtr < IDirect3DTexture9 > &texture = vmuTextures[i];
if (!vmu_lcd_status[i])
{
texture.reset();
continue;
}
if (texture == nullptr || vmu_lcd_changed[i])
{
device->CreateTexture(48, 32, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture.get(), 0);
D3DLOCKED_RECT rect;
if (SUCCEEDED(texture->LockRect(0, &rect, nullptr, 0)))
{
u8 *dst = (u8 *) rect.pBits;
for (int y = 0; y < 32; y++)
memcpy(dst + y * rect.Pitch, vmu_lcd_data[i] + (31 - y) * 48, 48 * 4);
texture->UnlockRect(0);
}
vmu_lcd_changed[i] = false;
}
float x;
if (i & 2)
x = width - vmu_padding - vmu_width;
else
x = vmu_padding;
float y;
if (i & 4)
{
y = height - vmu_padding - vmu_height;
if (i & 1)
y -= vmu_padding + vmu_height;
}
else
{
y = vmu_padding;
if (i & 1)
y += vmu_padding + vmu_height;
}
device->SetTexture(0, texture);
RECT rect { (long)x, (long)y, (long)(x + vmu_width), (long)(y + vmu_height) };
drawQuad(rect, D3DCOLOR_ARGB(192, 255, 255, 255));
}
}
if (crosshair)
{
if (!xhairTexture)
{
const u32* texData = getCrosshairTextureData();
device->CreateTexture(16, 16, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &xhairTexture.get(), 0);
D3DLOCKED_RECT rect;
if (SUCCEEDED(xhairTexture->LockRect(0, &rect, nullptr, 0)))
{
if (rect.Pitch == 16 * sizeof(u32))
memcpy(rect.pBits, texData, 16 * 16 * sizeof(u32));
else
{
u8 *dst = (u8 *) rect.pBits;
for (int y = 0; y < 16; y++)
memcpy(dst + y * rect.Pitch, texData + y * 16, 16 * sizeof(u32));
}
xhairTexture->UnlockRect(0);
}
}
device->SetTexture(0, xhairTexture);
for (u32 i = 0; i < config::CrosshairColor.size(); i++)
{
if (config::CrosshairColor[i] == 0)
continue;
if (settings.platform.system == DC_PLATFORM_DREAMCAST
&& config::MapleMainDevices[i] != MDT_LightGun)
continue;
float x, y;
std::tie(x, y) = getCrosshairPosition(i);
float halfWidth = XHAIR_WIDTH / 2.f;
RECT rect { (long) (x - halfWidth), (long) (y - halfWidth), (long) (x + halfWidth), (long) (y + halfWidth) };
D3DCOLOR color = (config::CrosshairColor[i] & 0xFF00FF00)
| ((config::CrosshairColor[i] >> 16) & 0xFF)
| ((config::CrosshairColor[i] & 0xFF) << 16);
drawQuad(rect, color);
}
}
}
void D3DOverlay::setupRenderState(u32 displayWidth, u32 displayHeight)
{
device->SetPixelShader(NULL);
device->SetVertexShader(NULL);
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
device->SetRenderState(D3DRS_ZENABLE, FALSE);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CONSTANT);
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CONSTANT);
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
glm::mat4 identity = glm::identity<glm::mat4>();
glm::mat4 projection = glm::translate(glm::vec3(-1.f - 1.f / displayWidth, 1.f + 1.f / displayHeight, 0))
* glm::scale(glm::vec3(2.f / displayWidth, -2.f / displayHeight, 1.f));
device->SetTransform(D3DTS_WORLD, (const D3DMATRIX *)&identity[0][0]);
device->SetTransform(D3DTS_VIEW, (const D3DMATRIX *)&identity[0][0]);
device->SetTransform(D3DTS_PROJECTION, (const D3DMATRIX *)&projection[0][0]);
device->SetFVF(D3DFVF_XYZ | D3DFVF_TEX1);
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
#include <windows.h>
#include <d3d9.h>
#include <array>
#include "comptr.h"
class D3DOverlay
{
public:
void init(const ComPtr<IDirect3DDevice9>& device) {
this->device = device;
}
void term() {
device.reset();
xhairTexture.reset();
for (auto& vmu : vmuTextures)
vmu.reset();
}
void draw(u32 width, u32 height, bool vmu, bool crosshair);
private:
void setupRenderState(u32 displayWidth, u32 displayHeight);
void drawQuad(const RECT& rect, D3DCOLOR color);
struct Vertex
{
float pos[3];
float uv[2];
};
ComPtr<IDirect3DDevice9> device;
ComPtr<IDirect3DTexture9> xhairTexture;
std::array<ComPtr<IDirect3DTexture9>, 8> vmuTextures;
};

View File

@ -225,7 +225,7 @@ u64 D3DRenderer::GetTexture(TSP tsp, TCW tcw)
{
texCache.DeleteLater(tf->texture);
tf->texture.reset();
tf->CheckCustomTexture();
tf->loadCustomTexture();
}
}
return (uintptr_t)tf->texture.get();
@ -250,7 +250,14 @@ void D3DRenderer::readDCFramebuffer()
D3DLOCKED_RECT rect;
dcfbTexture->LockRect(0, &rect, nullptr, 0);
memcpy(rect.pBits, pb.data(), width * height * 4);
if ((u32)rect.Pitch == width * sizeof(u32))
memcpy(rect.pBits, pb.data(), width * height * sizeof(u32));
else
{
u8 *dst = (u8 *)rect.pBits;
for (int y = 0; y < height; y++)
memcpy(dst + y * rect.Pitch, pb.data() + y * width, width * sizeof(u32));
}
dcfbTexture->UnlockRect(0);
}
@ -303,7 +310,7 @@ void D3DRenderer::setGPState(const PolyParam *gp)
float trilinear_alpha;
if (gp->pcw.Texture && gp->tsp.FilterMode > 1 && Type != ListType_Punch_Through && gp->tcw.MipMapped == 1)
{
trilinear_alpha = 0.25 * (gp->tsp.MipMapD & 0x3);
trilinear_alpha = 0.25f * (gp->tsp.MipMapD & 0x3);
if (gp->tsp.FilterMode == 2)
// Trilinear pass A
trilinear_alpha = 1.f - trilinear_alpha;
@ -340,15 +347,13 @@ void D3DRenderer::setGPState(const PolyParam *gp)
{
float paletteIndex[4];
if (gp->tcw.PixelFmt == PixelPal4)
paletteIndex[0] = gp->tcw.PalSelect << 4;
paletteIndex[0] = (float)(gp->tcw.PalSelect << 4);
else
paletteIndex[0] = (gp->tcw.PalSelect >> 4) << 8;
paletteIndex[0] = (float)((gp->tcw.PalSelect >> 4) << 8);
device->SetPixelShaderConstantF(0, paletteIndex, 1);
}
devCache.SetVertexShader(shaders.getVertexShader(gp->pcw.Gouraud));
devCache.SetRenderState(D3DRS_SHADEMODE, gp->pcw.Gouraud == 1 ? D3DSHADE_GOURAUD : D3DSHADE_FLAT);
devCache.SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
devCache.SetRenderState(D3DRS_CLIPPING, FALSE);
/* TODO
if (clipmode == TileClipping::Inside)
@ -395,7 +400,7 @@ void D3DRenderer::setGPState(const PolyParam *gp)
//bilinear filtering
devCache.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
devCache.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
devCache.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); // //LINEAR for Trilinear filtering
devCache.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); // LINEAR for Trilinear filtering
}
}
@ -849,10 +854,10 @@ void D3DRenderer::setBaseScissor()
}
else
{
fWidth = pvrrc.fb_X_CLIP.max - pvrrc.fb_X_CLIP.min + 1;
fHeight = pvrrc.fb_Y_CLIP.max - pvrrc.fb_Y_CLIP.min + 1;
min_x = pvrrc.fb_X_CLIP.min;
min_y = pvrrc.fb_Y_CLIP.min;
fWidth = (float)(pvrrc.fb_X_CLIP.max - pvrrc.fb_X_CLIP.min + 1);
fHeight = (float)(pvrrc.fb_Y_CLIP.max - pvrrc.fb_Y_CLIP.min + 1);
min_x = (float)pvrrc.fb_X_CLIP.min;
min_y = (float)pvrrc.fb_Y_CLIP.min;
if (config::RenderToTextureUpscale > 1 && !config::RenderToTextureBuffer)
{
min_x *= config::RenderToTextureUpscale;
@ -1010,7 +1015,10 @@ bool D3DRenderer::Render()
}
verifyWin(device->SetDepthStencilSurface(depthSurface));
matrices.CalcMatrices(&pvrrc, width, height);
verifyWin(device->SetVertexShaderConstantF(0, &matrices.GetNormalMatrix()[0][0], 4));
// infamous DX9 half-pixel viewport shift
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/directly-mapping-texels-to-pixels
glm::mat4 normalMat = glm::translate(glm::vec3(-1.f / width, 1.f / height, 0)) * matrices.GetNormalMatrix();
verifyWin(device->SetVertexShaderConstantF(0, &normalMat[0][0], 4));
devCache.reset();
devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
@ -1055,7 +1063,7 @@ bool D3DRenderer::Render()
u8* fog_density = (u8*)&FOG_DENSITY;
float fog_den_mant = fog_density[1] / 128.0f; //bit 7 -> x. bit, so [6:0] -> fraction -> /128
s32 fog_den_exp = (s8)fog_density[0];
float fog_den_float = fog_den_mant * powf(2.0f, fog_den_exp) * config::ExtraDepthScale;
float fog_den_float = fog_den_mant * powf(2.0f, (float)fog_den_exp) * config::ExtraDepthScale;
float fogDensityAndScale[4]= { fog_den_float, 1.f - FPU_SHAD_SCALE.scale_factor / 256.f, 0, 1 };
device->SetPixelShaderConstantF(3, fogDensityAndScale, 1);
@ -1084,10 +1092,10 @@ bool D3DRenderer::Render()
devCache.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
devCache.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
devCache.SetRenderState(D3DRS_TEXTUREFACTOR, 0xFFFFFFFF);
devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
devCache.SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
devCache.SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
devCache.SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
setBaseScissor();
@ -1186,8 +1194,15 @@ void D3DRenderer::updatePaletteTexture()
D3DLOCKED_RECT rect;
verifyWin(paletteTexture->LockRect(0, &rect, nullptr, 0));
verify(rect.Pitch == 32 * 4);
memcpy(rect.pBits, palette32_ram, 32 * 32 * 4);
if (rect.Pitch == 32 * sizeof(u32))
memcpy(rect.pBits, palette32_ram, 32 * 32 * sizeof(u32));
else
{
u8 *dst = (u8 *)rect.pBits;
for (int y = 0; y < 32; y++)
memcpy(dst + y * rect.Pitch, palette32_ram + y * 32, 32 * sizeof(u32));
}
paletteTexture->UnlockRect(0);
device->SetTexture(1, paletteTexture);
}
@ -1202,8 +1217,14 @@ void D3DRenderer::updateFogTexture()
D3DLOCKED_RECT rect;
verifyWin(fogTexture->LockRect(0, &rect, nullptr, 0));
verify(rect.Pitch == 128);
memcpy(rect.pBits, temp_tex_buffer, 128 * 2 * 1);
if (rect.Pitch == 128)
memcpy(rect.pBits, temp_tex_buffer, 128 * 2 * 1);
else
{
u8 *dst = (u8 *)rect.pBits;
for (int y = 0; y < 2; y++)
memcpy(dst + y * rect.Pitch, temp_tex_buffer + y * 128, 128);
}
fogTexture->UnlockRect(0);
device->SetTexture(2, fogTexture);
}

View File

@ -264,7 +264,7 @@ const ComPtr<IDirect3DPixelShader9>& D3DShaders::getShader(bool pp_Texture, bool
bool pp_Offset, u32 pp_FogCtrl, bool pp_BumpMap, bool fog_clamping,
bool trilinear, bool palette, bool gouraud)
{
u32 hash = pp_Texture
u32 hash = (int)pp_Texture
| (pp_UseAlpha << 1)
| (pp_IgnoreTexA << 2)
| (pp_ShadInstr << 3)

View File

@ -44,52 +44,66 @@ void D3DTexture::UploadToGPU(int width, int height, u8* temp_tex_buffer, bool mi
default:
return;
}
int mipmapLevels = 1;
if (mipmapsIncluded)
{
// TODO Upload all mipmap levels
int mipmapLevels = 0;
mipmapLevels = 0;
int dim = width;
while (dim != 0)
{
mipmapLevels++;
dim >>= 1;
}
for (int i = 0; i < mipmapLevels - 1; i++)
temp_tex_buffer += (1 << (2 * i)) * bpp;
}
D3DLOCKED_RECT rect;
while (true)
{
if (texture == nullptr)
{
if (mipmapped)
theDXContext.getDevice()->CreateTexture(width, height, 0, D3DUSAGE_AUTOGENMIPMAP, d3dFormat, D3DPOOL_MANAGED, &texture.get(), 0);
else
theDXContext.getDevice()->CreateTexture(width, height, 1, 0, d3dFormat, D3DPOOL_MANAGED, &texture.get(), 0);
u32 levels = mipmapLevels;
u32 usage = 0;
if (mipmapped && !mipmapsIncluded)
{
levels = 0;
usage = D3DUSAGE_AUTOGENMIPMAP;
}
theDXContext.getDevice()->CreateTexture(width, height, levels, usage, d3dFormat, D3DPOOL_MANAGED, &texture.get(), 0); // TODO the managed pool persists between device resets
verify(texture != nullptr);
}
if (SUCCEEDED(texture->LockRect(0, &rect, nullptr, 0)))
if (SUCCEEDED(texture->LockRect(mipmapLevels - 1, &rect, nullptr, 0)))
break;
D3DSURFACE_DESC desc;
texture->GetLevelDesc(0, &desc);
if (desc.Pool != D3DPOOL_DEFAULT)
// it should be lockable so error out
return;
// RTT targets are created in the default pool and aren't lockable, so delete it and recreate it in the managed pool
texture.reset();
}
if (width * bpp == (u32)rect.Pitch)
memcpy(rect.pBits, temp_tex_buffer, width * bpp * height);
else
for (int i = 0; i < mipmapLevels; i++)
{
u8 *dst = (u8 *)rect.pBits;
for (int l = 0; l < height; l++)
u32 w = mipmapLevels == 1 ? width : 1 << i;
u32 h = mipmapLevels == 1 ? height : 1 << i;
if (w * bpp == (u32)rect.Pitch)
memcpy(rect.pBits, temp_tex_buffer, w * bpp * h);
else
{
memcpy(dst, temp_tex_buffer, width * bpp);
dst += rect.Pitch;
temp_tex_buffer += width * bpp;
u8 *dst = (u8 *)rect.pBits;
u8 *src = temp_tex_buffer;
for (u32 l = 0; l < h; l++)
{
memcpy(dst, src, w * bpp);
dst += rect.Pitch;
src += w * bpp;
}
}
texture->UnlockRect(mipmapLevels - i - 1);
temp_tex_buffer += (1 << (2 * i)) * bpp;
if (i < mipmapLevels - 1)
if (FAILED(texture->LockRect(mipmapLevels - i - 2, &rect, nullptr, 0)))
break;
}
texture->UnlockRect(0);
}
bool D3DTexture::Delete()
@ -100,3 +114,16 @@ bool D3DTexture::Delete()
texture.reset();
return true;
}
void D3DTexture::loadCustomTexture()
{
u32 size = custom_width * custom_height;
u8 *p = custom_image_data;
while (size--)
{
// RGBA -> BGRA
std::swap(p[0], p[2]);
p += 4;
}
CheckCustomTexture();
}

View File

@ -28,13 +28,14 @@ public:
void UploadToGPU(int width, int height, u8* temp_tex_buffer, bool mipmapped,
bool mipmapsIncluded = false) override;
bool Delete() override;
void loadCustomTexture();
};
class D3DTextureCache final : public BaseTextureCache<D3DTexture>
{
public:
D3DTextureCache() {
D3DTexture::SetDirectXColorOrder(true); // TODO need to be reset to false by other renderers
D3DTexture::SetDirectXColorOrder(true);
}
~D3DTextureCache() {
Clear();

View File

@ -48,11 +48,13 @@ bool DXContext::Init()
D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &pDevice.get())))
return false;
gui_init();
overlay.init(pDevice);
return ImGui_ImplDX9_Init(pDevice.get());
}
void DXContext::Term()
{
overlay.term();
ImGui_ImplDX9_Shutdown();
pDevice.reset();
pD3D.reset();
@ -78,7 +80,7 @@ void DXContext::EndImGuiFrame()
pDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
pDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
if (!overlay)
if (!overlayOnly)
{
pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(0, 0, 0, 255), 1.0f, 0);
if (renderer != nullptr)
@ -86,6 +88,15 @@ void DXContext::EndImGuiFrame()
}
if (SUCCEEDED(pDevice->BeginScene()))
{
if (overlayOnly)
{
if (crosshairsNeeded() || config::FloatVMUs)
overlay.draw(screen_width, screen_height, config::FloatVMUs, true);
}
else
{
overlay.draw(screen_width, screen_height, true, false);
}
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
pDevice->EndScene();
}
@ -109,6 +120,7 @@ void DXContext::resetDevice()
{
if (renderer != nullptr)
((D3DRenderer *)renderer)->preReset();
overlay.term();
ImGui_ImplDX9_InvalidateDeviceObjects();
HRESULT hr = pDevice->Reset(&d3dpp);
if (hr == D3DERR_INVALIDCALL)
@ -117,6 +129,7 @@ void DXContext::resetDevice()
return;
}
ImGui_ImplDX9_CreateDeviceObjects();
overlay.init(pDevice);
if (renderer != nullptr)
((D3DRenderer *)renderer)->postReset();
}

View File

@ -18,61 +18,12 @@
*/
#pragma once
#ifdef _WIN32
#include "types.h"
#include <windows.h>
#include <d3d9.h>
#include "imgui_impl_dx9.h"
#include "types.h"
template<typename T>
class ComPtr
{
public:
ComPtr() = default;
ComPtr(const ComPtr& other) : ptr(other.ptr) {
if (ptr != nullptr)
ptr->AddRef();
}
ComPtr(ComPtr&& other) noexcept {
std::swap(ptr, other.ptr);
}
~ComPtr() {
if (ptr != nullptr)
ptr->Release();
}
ComPtr& operator=(const ComPtr& other) {
if (this != &other)
*this = ComPtr(other);
return *this;
}
ComPtr& operator=(ComPtr&& other) noexcept {
std::swap(ptr, other.ptr);
return *this;
}
T* operator->() const noexcept {
return ptr;
}
explicit operator bool() const noexcept {
return ptr != nullptr;
}
operator T*() const noexcept {
return ptr;
}
T*& get() noexcept {
return ptr;
}
void reset(T *ptr = nullptr) {
if (ptr == this->ptr)
return;
std::swap(this->ptr, ptr);
if (ptr != nullptr)
ptr->Release();
}
private:
T *ptr = nullptr;
};
#include "comptr.h"
#include "d3d_overlay.h"
class DXContext
{
@ -84,7 +35,7 @@ public:
const ComPtr<IDirect3D9>& getD3D() const { return pD3D; }
const ComPtr<IDirect3DDevice9>& getDevice() const { return pDevice; }
void resize();
void setOverlay(bool overlay) { this->overlay = overlay; }
void setOverlay(bool overlayOnly) { this->overlayOnly = overlayOnly; }
std::string getDriverName() const {
D3DADAPTER_IDENTIFIER9 id;
pD3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &id);
@ -106,8 +57,9 @@ private:
ComPtr<IDirect3D9> pD3D;
ComPtr<IDirect3DDevice9> pDevice;
D3DPRESENT_PARAMETERS d3dpp{};
bool overlay = false;
bool overlayOnly = false;
HWND hWnd = nullptr;
D3DOverlay overlay;
};
extern DXContext theDXContext;
#endif

View File

@ -127,17 +127,6 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
return;
}
// Backup the DX9 state
IDirect3DStateBlock9* d3d9_state_block = NULL;
if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
return;
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
D3DMATRIX last_world, last_view, last_projection;
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
// Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format.
// FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and
// 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR
@ -211,15 +200,6 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore the DX9 transform
g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
// Restore the DX9 state
d3d9_state_block->Apply();
d3d9_state_block->Release();
}
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)