Feature enablement: This hooks up the initial WIP direct3d 9 stuff
This is still rough, it's not complete, and barely displays things. At the very least it's still a work in progress and shouldn't be enabled until things begin to work as intended Signed-off-by: Zach Bacon <zachbacon@vba-m.com>
This commit is contained in:
parent
5b4559f4ee
commit
70d9d5d7c0
|
@ -288,7 +288,9 @@ function(configure_wx_target target)
|
|||
endif()
|
||||
|
||||
# Direct3D.
|
||||
if(NOT ENABLE_DIRECT3D)
|
||||
if(ENABLE_DIRECT3D)
|
||||
_add_link_libraries(d3d9 d3dx9)
|
||||
else()
|
||||
_add_compile_definitions(NO_D3D)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ static const std::array<wxString, kNbInterframes> kInterframeStrings = {
|
|||
static const std::array<wxString, kNbRenderMethods> kRenderMethodStrings = {
|
||||
"simple",
|
||||
"opengl",
|
||||
#if defined(__WXMSW__) && !defined(NO_D3D)
|
||||
#ifndef NO_D3D
|
||||
"direct3d",
|
||||
#elif defined(__WXMAC__)
|
||||
"quartz2d",
|
||||
|
|
|
@ -66,7 +66,7 @@ static constexpr size_t kNbInterframes = static_cast<size_t>(Interframe::kLast);
|
|||
enum class RenderMethod {
|
||||
kSimple = 0,
|
||||
kOpenGL,
|
||||
#if defined(__WXMSW__) && !defined(NO_D3D)
|
||||
#ifndef NO_D3D
|
||||
kDirect3d,
|
||||
#elif defined(__WXMAC__)
|
||||
kQuartz2d,
|
||||
|
|
|
@ -278,7 +278,7 @@ DisplayConfig::DisplayConfig(wxWindow* parent)
|
|||
->SetValidator(RenderValidator(config::RenderMethod::kOpenGL));
|
||||
#endif // NO_OGL
|
||||
|
||||
#if defined(__WXMSW__) && !defined(NO_D3D)
|
||||
#ifndef NO_D3D
|
||||
// Enable the Direct3D option on Windows.
|
||||
GetValidatedChild("OutputDirect3D")
|
||||
->SetValidator(RenderValidator(config::RenderMethod::kDirect3d));
|
||||
|
|
|
@ -49,15 +49,46 @@ protected:
|
|||
};
|
||||
#endif
|
||||
|
||||
#if defined(__WXMSW__) && !defined(NO_D3D)
|
||||
class DXDrawingPanel : public DrawingPanel {
|
||||
public:
|
||||
DXDrawingPanel(wxWindow* parent, int _width, int _height);
|
||||
#ifndef NO_D3D
|
||||
#include <d3d9.h>
|
||||
#include <d3dx9.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
protected:
|
||||
void DrawArea(wxWindowDC&);
|
||||
};
|
||||
#endif
|
||||
class D3DDrawingPanel : public DrawingPanel {
|
||||
public:
|
||||
D3DDrawingPanel(wxWindow* parent, int width, int height);
|
||||
virtual ~D3DDrawingPanel();
|
||||
|
||||
protected:
|
||||
// Drawing methods
|
||||
void DrawArea(wxWindowDC& dc) override;
|
||||
void DrawArea(uint8_t** data);
|
||||
void DrawOSD(wxWindowDC& dc) override;
|
||||
void DrawOSD();
|
||||
|
||||
// Event handlers
|
||||
void OnSize(wxSizeEvent& ev) override;
|
||||
void PaintEv(wxPaintEvent& ev) override;
|
||||
void EraseBackground(wxEraseEvent& ev) override;
|
||||
|
||||
// D3D management
|
||||
void DrawingPanelInit() override;
|
||||
void UpdateTexture();
|
||||
void AdjustViewport();
|
||||
void Render();
|
||||
|
||||
private:
|
||||
// Direct3D resources
|
||||
IDirect3D9* d3d_;
|
||||
IDirect3DDevice9* device_;
|
||||
IDirect3DTexture9* texture_;
|
||||
IDirect3DVertexBuffer9* vertex_buffer_;
|
||||
ID3DXFont* font_;
|
||||
D3DPRESENT_PARAMETERS d3dpp_;
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(__WXMAC__)
|
||||
class Quartz2DDrawingPanel : public BasicDrawingPanel {
|
||||
|
|
698
src/wx/panel.cpp
698
src/wx/panel.cpp
|
@ -1175,9 +1175,9 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
|||
panel = new GLDrawingPanel(this, basic_width, basic_height);
|
||||
break;
|
||||
#endif
|
||||
#if defined(__WXMSW__) && !defined(NO_D3D)
|
||||
#ifndef NO_D3D
|
||||
case config::RenderMethod::kDirect3d:
|
||||
panel = new DXDrawingPanel(this, basic_width, basic_height);
|
||||
panel = new D3DDrawingPanel(this, basic_width, basic_height);
|
||||
break;
|
||||
#endif
|
||||
case config::RenderMethod::kLast:
|
||||
|
@ -2420,28 +2420,690 @@ void GLDrawingPanel::DrawArea(wxWindowDC& dc)
|
|||
|
||||
#endif // GL support
|
||||
|
||||
#if defined(__WXMSW__) && !defined(NO_D3D)
|
||||
#define DIRECT3D_VERSION 0x0900
|
||||
#include <d3d9.h> // main include file
|
||||
//#include <d3dx9core.h> // required for font rendering
|
||||
#include <dxerr9.h> // contains debug functions
|
||||
#ifndef NO_D3D
|
||||
|
||||
DXDrawingPanel::DXDrawingPanel(wxWindow* parent, int _width, int _height)
|
||||
: DrawingPanel(parent, _width, _height)
|
||||
{
|
||||
// FIXME: implement
|
||||
}
|
||||
BEGIN_EVENT_TABLE(D3DDrawingPanel, DrawingPanel)
|
||||
EVT_SIZE(D3DDrawingPanel::OnSize)
|
||||
EVT_PAINT(D3DDrawingPanel::PaintEv)
|
||||
EVT_ERASE_BACKGROUND(D3DDrawingPanel::EraseBackground)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
void DXDrawingPanel::DrawArea(wxWindowDC& dc)
|
||||
{
|
||||
// FIXME: implement
|
||||
if (!did_init) {
|
||||
DrawingPanelInit();
|
||||
D3DDrawingPanel::D3DDrawingPanel(wxWindow* parent, int _width, int _height)
|
||||
: DrawingPanel(parent, _width, _height),
|
||||
d3d_(nullptr),
|
||||
device_(nullptr),
|
||||
texture_(nullptr),
|
||||
font_(nullptr),
|
||||
vertex_buffer_(nullptr) {
|
||||
|
||||
memset(delta, 0xff, sizeof(delta));
|
||||
|
||||
if (OPTION(kDispFilter) == config::Filter::kPlugin) {
|
||||
rpi_ = widgets::MaybeLoadFilterPlugin(OPTION(kDispFilterPlugin),
|
||||
&filter_plugin_);
|
||||
if (rpi_) {
|
||||
rpi_->Flags &= ~RPI_565_SUPP;
|
||||
|
||||
if (rpi_->Flags & RPI_888_SUPP) {
|
||||
rpi_->Flags &= ~RPI_555_SUPP;
|
||||
// FIXME: should this be 32 or 24? No docs or sample source
|
||||
systemColorDepth = 32;
|
||||
} else
|
||||
systemColorDepth = 16;
|
||||
|
||||
if (!rpi_->Output) {
|
||||
// note that in actual kega fusion plugins, rpi_->Output is
|
||||
// unused (as is rpi_->Handle)
|
||||
rpi_->Output =
|
||||
(RENDPLUG_Output)filter_plugin_.GetSymbol("RenderPluginOutput");
|
||||
}
|
||||
scale *= (rpi_->Flags & RPI_OUT_SCLMSK) >> RPI_OUT_SCLSH;
|
||||
} else {
|
||||
// This is going to delete the object. Do nothing more here.
|
||||
OPTION(kDispFilterPlugin) = wxEmptyString;
|
||||
OPTION(kDispFilter) = config::Filter::kNone;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (todraw) {
|
||||
if (OPTION(kDispFilter) != config::Filter::kPlugin) {
|
||||
scale *= GetFilterScale();
|
||||
systemColorDepth = 32;
|
||||
}
|
||||
|
||||
// Initialize color tables
|
||||
if (systemColorDepth == 32) {
|
||||
#if wxBYTE_ORDER == wxLITTLE_ENDIAN
|
||||
systemRedShift = 3;
|
||||
systemGreenShift = 11;
|
||||
systemBlueShift = 19;
|
||||
RGB_LOW_BITS_MASK = 0x00010101;
|
||||
#else
|
||||
systemRedShift = 27;
|
||||
systemGreenShift = 19;
|
||||
systemBlueShift = 11;
|
||||
RGB_LOW_BITS_MASK = 0x01010100;
|
||||
#endif
|
||||
} else {
|
||||
// plugins expect RGB in native byte order
|
||||
systemRedShift = 10;
|
||||
systemGreenShift = 5;
|
||||
systemBlueShift = 0;
|
||||
RGB_LOW_BITS_MASK = 0x0421;
|
||||
}
|
||||
|
||||
// wxImage is 24-bit RGB, so 24-bit is preferred. Filters require 16 or 32, though
|
||||
if (OPTION(kDispFilter) == config::Filter::kNone &&
|
||||
OPTION(kDispIFB) == config::Interframe::kNone) {
|
||||
// changing from 32 to 24 does not require regenerating color tables
|
||||
systemColorDepth = 32;
|
||||
}
|
||||
|
||||
DrawingPanelInit();
|
||||
}
|
||||
|
||||
void D3DDrawingPanel::DrawingPanelInit()
|
||||
{
|
||||
// Initialize Direct3D
|
||||
d3d_ = Direct3DCreate9(D3D_SDK_VERSION);
|
||||
if (!d3d_) {
|
||||
wxLogError("Failed to create Direct3D9 object");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get current window size
|
||||
int client_width, client_height;
|
||||
widgets::GetRealPixelClientSize(this, &client_width, &client_height);
|
||||
|
||||
ZeroMemory(&d3dpp_, sizeof(d3dpp_));
|
||||
d3dpp_.Windowed = TRUE;
|
||||
d3dpp_.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||
d3dpp_.hDeviceWindow = GetHWND();
|
||||
d3dpp_.BackBufferFormat = D3DFMT_UNKNOWN;
|
||||
d3dpp_.BackBufferWidth = client_width;
|
||||
d3dpp_.BackBufferHeight = client_height;
|
||||
d3dpp_.EnableAutoDepthStencil = FALSE;
|
||||
d3dpp_.PresentationInterval = OPTION(kPrefVsync) ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||
|
||||
HRESULT hr = d3d_->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetHWND(),
|
||||
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp_, &device_);
|
||||
if (FAILED(hr)) {
|
||||
wxLogError("Failed to create Direct3D9 device: HRESULT 0x%08X", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up rendering states
|
||||
device_->SetRenderState(D3DRS_LIGHTING, FALSE);
|
||||
device_->SetRenderState(D3DRS_ZENABLE, FALSE);
|
||||
device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
device_->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
|
||||
device_->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
device_->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
|
||||
|
||||
// Set up alpha blending for OSD text
|
||||
device_->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
||||
device_->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
device_->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
|
||||
// Set filtering mode based on bilinear option
|
||||
DWORD filter = OPTION(kDispBilinear) ? D3DTEXF_LINEAR : D3DTEXF_POINT;
|
||||
device_->SetSamplerState(0, D3DSAMP_MINFILTER, filter);
|
||||
device_->SetSamplerState(0, D3DSAMP_MAGFILTER, filter);
|
||||
|
||||
// Create a vertex buffer for our quad
|
||||
struct VERTEX { float x, y, z, rhw; float u, v; };
|
||||
hr = device_->CreateVertexBuffer(4 * sizeof(VERTEX), 0, D3DFVF_XYZRHW | D3DFVF_TEX1,
|
||||
D3DPOOL_MANAGED, &vertex_buffer_, NULL);
|
||||
if (FAILED(hr)) {
|
||||
wxLogError("Failed to create vertex buffer: HRESULT 0x%08X", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create font for OSD
|
||||
D3DXFONT_DESC font_desc;
|
||||
ZeroMemory(&font_desc, sizeof(font_desc));
|
||||
font_desc.Height = 24;
|
||||
font_desc.Width = 0;
|
||||
font_desc.Weight = FW_NORMAL;
|
||||
font_desc.MipLevels = 1;
|
||||
font_desc.Italic = FALSE;
|
||||
font_desc.CharSet = DEFAULT_CHARSET;
|
||||
font_desc.OutputPrecision = OUT_DEFAULT_PRECIS;
|
||||
font_desc.Quality = DEFAULT_QUALITY;
|
||||
font_desc.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
|
||||
wcscpy(font_desc.FaceName, L"Arial");
|
||||
|
||||
hr = D3DXCreateFontIndirect(device_, &font_desc, &font_);
|
||||
if (FAILED(hr)) {
|
||||
wxLogError("Failed to create Direct3D9 font: HRESULT 0x%08X", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
did_init = true;
|
||||
}
|
||||
|
||||
void D3DDrawingPanel::PaintEv(wxPaintEvent& ev)
|
||||
{
|
||||
(void)ev; // unused params
|
||||
wxPaintDC dc(GetWindow());
|
||||
|
||||
if (!todraw) {
|
||||
// since this is set for custom background, not drawing anything
|
||||
// will cause garbage to be displayed, so draw a black area
|
||||
draw_black_background(GetWindow());
|
||||
return;
|
||||
}
|
||||
|
||||
DrawArea(dc);
|
||||
}
|
||||
|
||||
void D3DDrawingPanel::EraseBackground(wxEraseEvent& ev)
|
||||
{
|
||||
(void)ev; // unused params
|
||||
// do nothing, do not allow propagation
|
||||
}
|
||||
|
||||
void D3DDrawingPanel::DrawArea(wxWindowDC& dc)
|
||||
{
|
||||
(void)dc; // unused params - D3D doesn't use wx DC
|
||||
|
||||
if (!device_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Begin the scene
|
||||
if (SUCCEEDED(device_->BeginScene())) {
|
||||
device_->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
|
||||
|
||||
// Only render if we have valid data
|
||||
if (todraw && texture_) {
|
||||
// Setup quadrilateral to render the texture
|
||||
D3DVIEWPORT9 viewport;
|
||||
device_->GetViewport(&viewport);
|
||||
|
||||
// Calculate aspect ratio and scaling
|
||||
float dest_width, dest_height;
|
||||
float src_aspect = (float)(width * scale) / (float)(height * scale);
|
||||
float screen_aspect = (float)viewport.Width / (float)viewport.Height;
|
||||
|
||||
if (OPTION(kDispStretch)) {
|
||||
// Stretch to fill screen
|
||||
dest_width = (float)viewport.Width;
|
||||
dest_height = (float)viewport.Height;
|
||||
} else {
|
||||
// Maintain aspect ratio
|
||||
if (src_aspect > screen_aspect) {
|
||||
// Width constrained
|
||||
dest_width = (float)viewport.Width;
|
||||
dest_height = dest_width / src_aspect;
|
||||
} else {
|
||||
// Height constrained
|
||||
dest_height = (float)viewport.Height;
|
||||
dest_width = dest_height * src_aspect;
|
||||
}
|
||||
}
|
||||
|
||||
// Center the image
|
||||
float x = (viewport.Width - dest_width) / 2.0f;
|
||||
float y = (viewport.Height - dest_height) / 2.0f;
|
||||
|
||||
// Update vertex buffer with new coordinates
|
||||
struct VERTEX { float x, y, z, rhw; float u, v; };
|
||||
VERTEX* vertices;
|
||||
vertex_buffer_->Lock(0, 0, (void**)&vertices, 0);
|
||||
|
||||
// Setup vertices for a quad
|
||||
// Top-left
|
||||
vertices[0].x = x;
|
||||
vertices[0].y = y;
|
||||
vertices[0].z = 0.0f;
|
||||
vertices[0].rhw = 1.0f;
|
||||
vertices[0].u = 0.0f;
|
||||
vertices[0].v = 0.0f;
|
||||
|
||||
// Top-right
|
||||
vertices[1].x = x + dest_width;
|
||||
vertices[1].y = y;
|
||||
vertices[1].z = 0.0f;
|
||||
vertices[1].rhw = 1.0f;
|
||||
vertices[1].u = 1.0f;
|
||||
vertices[1].v = 0.0f;
|
||||
|
||||
// Bottom-left
|
||||
vertices[2].x = x;
|
||||
vertices[2].y = y + dest_height;
|
||||
vertices[2].z = 0.0f;
|
||||
vertices[2].rhw = 1.0f;
|
||||
vertices[2].u = 0.0f;
|
||||
vertices[2].v = 1.0f;
|
||||
|
||||
// Bottom-right
|
||||
vertices[3].x = x + dest_width;
|
||||
vertices[3].y = y + dest_height;
|
||||
vertices[3].z = 0.0f;
|
||||
vertices[3].rhw = 1.0f;
|
||||
vertices[3].u = 1.0f;
|
||||
vertices[3].v = 1.0f;
|
||||
|
||||
vertex_buffer_->Unlock();
|
||||
|
||||
// Set the vertex buffer as the data source
|
||||
device_->SetStreamSource(0, vertex_buffer_, 0, sizeof(VERTEX));
|
||||
device_->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
|
||||
device_->SetTexture(0, texture_);
|
||||
|
||||
// Draw the quad
|
||||
device_->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
||||
|
||||
// Reset texture to avoid affecting other rendering
|
||||
device_->SetTexture(0, NULL);
|
||||
}
|
||||
|
||||
// Draw OSD text if needed
|
||||
DrawOSD();
|
||||
|
||||
// End the scene
|
||||
device_->EndScene();
|
||||
}
|
||||
|
||||
// Present the scene
|
||||
device_->Present(NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void D3DDrawingPanel::DrawArea(uint8_t** data)
|
||||
{
|
||||
// double-buffer buffer:
|
||||
// if filtering, this is filter output, retained for redraws
|
||||
// if not filtering, we still retain current image for redraws
|
||||
int outbpp = out_16 ? 2 : systemColorDepth == 24 ? 3 : 4;
|
||||
int outrb = systemColorDepth == 24 ? 0 : 4;
|
||||
int outstride = std::ceil(width * outbpp * scale) + outrb;
|
||||
|
||||
if (!pixbuf2) {
|
||||
int allocstride = outstride, alloch = height;
|
||||
|
||||
// gb may write borders, so allocate enough for them
|
||||
if (width == GameArea::GBWidth && height == GameArea::GBHeight) {
|
||||
allocstride = std::ceil(GameArea::SGBWidth * outbpp * scale) + outrb;
|
||||
alloch = GameArea::SGBHeight;
|
||||
}
|
||||
|
||||
pixbuf2 = (uint8_t*)calloc(allocstride, std::ceil((alloch + 2) * scale));
|
||||
}
|
||||
|
||||
if (OPTION(kDispFilter) == config::Filter::kNone) {
|
||||
todraw = *data;
|
||||
// *data is assigned below, after old buf has been processed
|
||||
pixbuf1 = pixbuf2;
|
||||
pixbuf2 = todraw;
|
||||
} else
|
||||
todraw = pixbuf2;
|
||||
|
||||
// FIXME: filters race condition?
|
||||
const int max_threads = 1;
|
||||
|
||||
// First, apply filters, if applicable, in parallel, if enabled
|
||||
// FIXME: && (gopts.ifb != FF_MOTION_BLUR || !renderer_can_motion_blur)
|
||||
if (OPTION(kDispFilter) != config::Filter::kNone ||
|
||||
OPTION(kDispIFB) != config::Interframe::kNone) {
|
||||
if (nthreads != max_threads) {
|
||||
if (nthreads) {
|
||||
if (nthreads > 1)
|
||||
for (int i = 0; i < nthreads; i++) {
|
||||
threads[i].lock_.Lock();
|
||||
threads[i].src_ = NULL;
|
||||
threads[i].sig_.Signal();
|
||||
threads[i].lock_.Unlock();
|
||||
threads[i].Wait();
|
||||
}
|
||||
|
||||
delete[] threads;
|
||||
}
|
||||
|
||||
nthreads = max_threads;
|
||||
threads = new FilterThread[nthreads];
|
||||
// first time around, no threading in order to avoid
|
||||
// static initializer conflicts
|
||||
threads[0].threadno_ = 0;
|
||||
threads[0].nthreads_ = 1;
|
||||
threads[0].width_ = width;
|
||||
threads[0].height_ = height;
|
||||
threads[0].scale_ = scale;
|
||||
threads[0].src_ = *data;
|
||||
threads[0].dst_ = todraw;
|
||||
threads[0].delta_ = delta;
|
||||
threads[0].rpi_ = rpi_;
|
||||
threads[0].Entry();
|
||||
|
||||
// go ahead and start the threads up, though
|
||||
if (nthreads > 1) {
|
||||
for (int i = 0; i < nthreads; i++) {
|
||||
threads[i].threadno_ = i;
|
||||
threads[i].nthreads_ = nthreads;
|
||||
threads[i].width_ = width;
|
||||
threads[i].height_ = height;
|
||||
threads[i].scale_ = scale;
|
||||
threads[i].dst_ = todraw;
|
||||
threads[i].delta_ = delta;
|
||||
threads[i].rpi_ = rpi_;
|
||||
threads[i].done_ = &filt_done;
|
||||
threads[i].lock_.Lock();
|
||||
threads[i].Create();
|
||||
threads[i].Run();
|
||||
}
|
||||
}
|
||||
} else if (nthreads == 1) {
|
||||
threads[0].threadno_ = 0;
|
||||
threads[0].nthreads_ = 1;
|
||||
threads[0].width_ = width;
|
||||
threads[0].height_ = height;
|
||||
threads[0].scale_ = scale;
|
||||
threads[0].src_ = *data;
|
||||
threads[0].dst_ = todraw;
|
||||
threads[0].delta_ = delta;
|
||||
threads[0].rpi_ = rpi_;
|
||||
threads[0].Entry();
|
||||
} else {
|
||||
for (int i = 0; i < nthreads; i++) {
|
||||
threads[i].lock_.Lock();
|
||||
threads[i].src_ = *data;
|
||||
threads[i].sig_.Signal();
|
||||
threads[i].lock_.Unlock();
|
||||
}
|
||||
|
||||
for (int i = 0; i < nthreads; i++)
|
||||
filt_done.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
// swap buffers now that src has been processed
|
||||
if (OPTION(kDispFilter) == config::Filter::kNone) {
|
||||
*data = pixbuf1;
|
||||
}
|
||||
|
||||
// draw OSD text old-style (directly into output buffer), if needed
|
||||
// new style flickers too much, so we'll stick to this for now
|
||||
if (wxGetApp().frame->IsFullScreen() || !OPTION(kPrefDisableStatus)) {
|
||||
GameArea* panel = wxGetApp().frame->GetPanel();
|
||||
|
||||
if (panel->osdstat.size())
|
||||
drawText(todraw + outstride * (systemColorDepth != 24), outstride,
|
||||
10, 20, UTF8(panel->osdstat), OPTION(kPrefShowSpeedTransparent));
|
||||
|
||||
if (!panel->osdtext.empty()) {
|
||||
if (systemGetClock() - panel->osdtime < OSD_TIME) {
|
||||
wxString message = panel->osdtext;
|
||||
int linelen = std::ceil(width * scale - 20) / 8;
|
||||
int nlines = (message.size() + linelen - 1) / linelen;
|
||||
int cury = height - 14 - nlines * 10;
|
||||
char* buf = strdup(UTF8(message));
|
||||
char* ptr = buf;
|
||||
|
||||
while (nlines > 1) {
|
||||
char lchar = ptr[linelen];
|
||||
ptr[linelen] = 0;
|
||||
drawText(todraw + outstride * (systemColorDepth != 24),
|
||||
outstride, 10, cury, ptr,
|
||||
OPTION(kPrefShowSpeedTransparent));
|
||||
cury += 10;
|
||||
nlines--;
|
||||
ptr += linelen;
|
||||
*ptr = lchar;
|
||||
}
|
||||
|
||||
drawText(todraw + outstride * (systemColorDepth != 24),
|
||||
outstride, 10, cury, ptr,
|
||||
OPTION(kPrefShowSpeedTransparent));
|
||||
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
} else
|
||||
panel->osdtext.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Create or update texture with the new frame data
|
||||
UpdateTexture();
|
||||
|
||||
// Draw the current frame
|
||||
wxClientDC dc(GetWindow());
|
||||
DrawArea(dc);
|
||||
}
|
||||
|
||||
void D3DDrawingPanel::UpdateTexture()
|
||||
{
|
||||
if (!device_ || !todraw) return;
|
||||
|
||||
// Calculate texture dimensions and format
|
||||
int tex_width = std::ceil(width * scale);
|
||||
int tex_height = std::ceil(height * scale);
|
||||
D3DFORMAT format = out_16 ? D3DFMT_A1R5G5B5 : D3DFMT_A8R8G8B8;
|
||||
|
||||
// Release previous texture if it exists
|
||||
if (texture_) {
|
||||
texture_->Release();
|
||||
texture_ = nullptr;
|
||||
}
|
||||
|
||||
// Create new texture with appropriate dimensions
|
||||
HRESULT hr = device_->CreateTexture(
|
||||
tex_width, tex_height,
|
||||
1, 0, format,
|
||||
D3DPOOL_MANAGED, &texture_, NULL);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
wxLogError("Failed to create Direct3D texture: HRESULT 0x%08X", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock texture for writing
|
||||
D3DLOCKED_RECT locked_rect;
|
||||
if (FAILED(texture_->LockRect(0, &locked_rect, NULL, 0))) {
|
||||
wxLogError("Failed to lock texture");
|
||||
texture_->Release();
|
||||
texture_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
int outbpp = out_16 ? 2 : systemColorDepth == 24 ? 3 : 4;
|
||||
int outrb = systemColorDepth == 24 ? 0 : 4;
|
||||
int outstride = std::ceil(width * outbpp * scale) + outrb;
|
||||
|
||||
// Copy the pixel data to the texture
|
||||
if (out_16) {
|
||||
// 16-bit mode
|
||||
uint16_t* src = (uint16_t*)todraw;
|
||||
uint16_t* dst = (uint16_t*)locked_rect.pBits;
|
||||
int src_pitch = outstride / 2;
|
||||
int dst_pitch = locked_rect.Pitch / 2;
|
||||
|
||||
for (int y = 0; y < tex_height; y++) {
|
||||
memcpy(dst, src, tex_width * 2);
|
||||
src += src_pitch;
|
||||
dst += dst_pitch;
|
||||
}
|
||||
}
|
||||
else if (systemColorDepth == 24) {
|
||||
// 24-bit mode
|
||||
// Need to convert 24-bit to 32-bit for Direct3D
|
||||
uint8_t* src = todraw;
|
||||
uint8_t* dst = (uint8_t*)locked_rect.pBits;
|
||||
|
||||
for (int y = 0; y < tex_height; y++) {
|
||||
for (int x = 0; x < tex_width; x++) {
|
||||
// Read RGB from source
|
||||
uint8_t r = src[x * 3 + 0];
|
||||
uint8_t g = src[x * 3 + 1];
|
||||
uint8_t b = src[x * 3 + 2];
|
||||
|
||||
// Write ARGB to destination
|
||||
dst[x * 4 + 0] = b; // B
|
||||
dst[x * 4 + 1] = g; // G
|
||||
dst[x * 4 + 2] = r; // R
|
||||
dst[x * 4 + 3] = 255; // A
|
||||
}
|
||||
|
||||
src += outstride;
|
||||
dst += locked_rect.Pitch;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 32-bit mode
|
||||
uint32_t* src = (uint32_t*)todraw;
|
||||
uint32_t* dst = (uint32_t*)locked_rect.pBits;
|
||||
int src_pitch = outstride / 4;
|
||||
int dst_pitch = locked_rect.Pitch / 4;
|
||||
|
||||
for (int y = 0; y < tex_height; y++) {
|
||||
for (int x = 0; x < tex_width; x++) {
|
||||
// Convert from GBA RGBA to D3D ARGB
|
||||
uint32_t color = src[x];
|
||||
uint8_t r = (color >> (systemRedShift - 3)) & 0xFF;
|
||||
uint8_t g = (color >> (systemGreenShift - 3)) & 0xFF;
|
||||
uint8_t b = (color >> (systemBlueShift - 3)) & 0xFF;
|
||||
dst[x] = 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
src += src_pitch;
|
||||
dst += dst_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock the texture
|
||||
texture_->UnlockRect(0);
|
||||
}
|
||||
|
||||
void D3DDrawingPanel::OnSize(wxSizeEvent& ev)
|
||||
{
|
||||
if (!device_) {
|
||||
ev.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the new client size
|
||||
int client_width, client_height;
|
||||
widgets::GetRealPixelClientSize(this, &client_width, &client_height);
|
||||
|
||||
// Update Direct3D presentation parameters
|
||||
d3dpp_.BackBufferWidth = client_width;
|
||||
d3dpp_.BackBufferHeight = client_height;
|
||||
|
||||
// Reset the device with new parameters
|
||||
if (device_) {
|
||||
// Release all D3D resources before reset
|
||||
if (texture_) {
|
||||
texture_->Release();
|
||||
texture_ = nullptr;
|
||||
}
|
||||
|
||||
if (font_) {
|
||||
font_->OnLostDevice();
|
||||
}
|
||||
|
||||
// Reset the device
|
||||
HRESULT hr = device_->Reset(&d3dpp_);
|
||||
if (FAILED(hr)) {
|
||||
wxLogError("Failed to reset Direct3D device: HRESULT 0x%08X", hr);
|
||||
} else {
|
||||
// Reset was successful, restore resources
|
||||
if (font_) {
|
||||
font_->OnResetDevice();
|
||||
}
|
||||
|
||||
// Restore device states
|
||||
device_->SetRenderState(D3DRS_LIGHTING, FALSE);
|
||||
device_->SetRenderState(D3DRS_ZENABLE, FALSE);
|
||||
device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
device_->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
|
||||
device_->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
device_->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
|
||||
|
||||
device_->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
||||
device_->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
device_->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
|
||||
// Restore filtering settings
|
||||
DWORD filter = OPTION(kDispBilinear) ? D3DTEXF_LINEAR : D3DTEXF_POINT;
|
||||
device_->SetSamplerState(0, D3DSAMP_MINFILTER, filter);
|
||||
device_->SetSamplerState(0, D3DSAMP_MAGFILTER, filter);
|
||||
|
||||
// Recreate texture if we have data
|
||||
if (todraw) {
|
||||
UpdateTexture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
void D3DDrawingPanel::DrawOSD()
|
||||
{
|
||||
if (!device_ || !font_) return;
|
||||
|
||||
GameArea* panel = wxGetApp().frame->GetPanel();
|
||||
D3DCOLOR text_color = D3DCOLOR_ARGB(
|
||||
OPTION(kPrefShowSpeedTransparent) ? 128 : 255,
|
||||
255, 0, 0);
|
||||
|
||||
// Get viewport dimensions for text positioning
|
||||
D3DVIEWPORT9 viewport;
|
||||
device_->GetViewport(&viewport);
|
||||
|
||||
// Draw status text if available
|
||||
if (panel->osdstat.size()) {
|
||||
RECT text_rect = {10, 20, static_cast<LONG>(viewport.Width - 10), static_cast<LONG>(viewport.Height - 10)};
|
||||
font_->DrawTextA(NULL, panel->osdstat.c_str(), -1, &text_rect,
|
||||
DT_LEFT | DT_TOP, text_color);
|
||||
}
|
||||
|
||||
// Draw OSD messages if available and not expired
|
||||
if (!panel->osdtext.empty() && systemGetClock() - panel->osdtime < OSD_TIME) {
|
||||
RECT text_rect = {10, static_cast<LONG>(viewport.Height - 10 - 24), static_cast<LONG>(viewport.Width - 20), static_cast<LONG>(viewport.Height - 10)};
|
||||
font_->DrawTextA(NULL, panel->osdtext.c_str(), -1, &text_rect,
|
||||
DT_LEFT | DT_BOTTOM, text_color);
|
||||
}
|
||||
}
|
||||
|
||||
void D3DDrawingPanel::DrawOSD(wxWindowDC& dc)
|
||||
{
|
||||
// For D3D, we render OSD directly in DrawArea method
|
||||
// This function just satisfies the interface requirement
|
||||
(void)dc; // unused param
|
||||
}
|
||||
|
||||
D3DDrawingPanel::~D3DDrawingPanel()
|
||||
{
|
||||
// Release Direct3D resources
|
||||
if (texture_) {
|
||||
texture_->Release();
|
||||
texture_ = nullptr;
|
||||
}
|
||||
|
||||
if (vertex_buffer_) {
|
||||
vertex_buffer_->Release();
|
||||
vertex_buffer_ = nullptr;
|
||||
}
|
||||
|
||||
if (font_) {
|
||||
font_->Release();
|
||||
font_ = nullptr;
|
||||
}
|
||||
|
||||
if (device_) {
|
||||
device_->Release();
|
||||
device_ = nullptr;
|
||||
}
|
||||
|
||||
if (d3d_) {
|
||||
d3d_->Release();
|
||||
d3d_ = nullptr;
|
||||
}
|
||||
|
||||
// Base class destructor takes care of other resources
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef NO_FFMPEG
|
||||
|
|
Loading…
Reference in New Issue