diff --git a/Source/Core/VideoCommon/Src/VideoCommon.h b/Source/Core/VideoCommon/Src/VideoCommon.h index b215da112d..6aa01dfa68 100644 --- a/Source/Core/VideoCommon/Src/VideoCommon.h +++ b/Source/Core/VideoCommon/Src/VideoCommon.h @@ -124,7 +124,7 @@ struct TargetRectangle : public MathUtil::Rectangle { #ifdef _WIN32 // Only used by D3D plugin. - const RECT *AsRECT() { + const RECT *AsRECT() const { // The types are binary compatible so this works. return (const RECT *)this; } diff --git a/Source/Dolphin.sln b/Source/Dolphin.sln index 753df1fce6..f87fb1956b 100644 --- a/Source/Dolphin.sln +++ b/Source/Dolphin.sln @@ -270,6 +270,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CLRun", "..\Externals\CLRun EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SVNRevGen", "Core\Common\SVNRevGen.vcproj", "{B807E8DB-4241-4754-BC2A-2F435BCA881A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_VideoMerge", "Plugins\Plugin_VideoMerge\Plugin_VideoMerge.vcproj", "{CA7F67A1-7DD9-4C49-94B8-F62AF3D4C72E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -676,6 +678,12 @@ Global {B807E8DB-4241-4754-BC2A-2F435BCA881A}.Release|Win32.Build.0 = Release|Win32 {B807E8DB-4241-4754-BC2A-2F435BCA881A}.Release|x64.ActiveCfg = Release|Win32 {B807E8DB-4241-4754-BC2A-2F435BCA881A}.Release|x64.Build.0 = Release|Win32 + {CA7F67A1-7DD9-4C49-94B8-F62AF3D4C72E}.Debug|Win32.ActiveCfg = Debug|Win32 + {CA7F67A1-7DD9-4C49-94B8-F62AF3D4C72E}.Debug|x64.ActiveCfg = Debug|x64 + {CA7F67A1-7DD9-4C49-94B8-F62AF3D4C72E}.DebugFast|Win32.ActiveCfg = Debug|x64 + {CA7F67A1-7DD9-4C49-94B8-F62AF3D4C72E}.DebugFast|x64.ActiveCfg = Debug|x64 + {CA7F67A1-7DD9-4C49-94B8-F62AF3D4C72E}.Release|Win32.ActiveCfg = Release|Win32 + {CA7F67A1-7DD9-4C49-94B8-F62AF3D4C72E}.Release|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/Plugins/Plugin_VideoMerge/Plugin_VideoMerge.vcproj b/Source/Plugins/Plugin_VideoMerge/Plugin_VideoMerge.vcproj new file mode 100644 index 0000000000..ea965c71ce --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Plugin_VideoMerge.vcproj @@ -0,0 +1,808 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Plugins/Plugin_VideoMerge/Src/BPFunctions.cpp b/Source/Plugins/Plugin_VideoMerge/Src/BPFunctions.cpp new file mode 100644 index 0000000000..1c16a8d285 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/BPFunctions.cpp @@ -0,0 +1,144 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Common +#include "Common.h" + +// VideoCommon +#include "VideoConfig.h" +#include "BPFunctions.h" +#include "Renderer.h" +#include "VertexShaderManager.h" +#include "VertexManager.h" + +#include "Main.h" + +namespace BPFunctions +{ + +void FlushPipeline() +{ + g_vertex_manager->Flush(); +} + +void SetGenerationMode(const BPCmd &bp) +{ + g_renderer->SetGenerationMode(); +} + +void SetScissor(const BPCmd &bp) +{ + g_renderer->SetScissorRect(); +} + +void SetLineWidth(const BPCmd &bp) +{ + g_renderer->SetLineWidth(); +} + +void SetDepthMode(const BPCmd &bp) +{ + g_renderer->SetDepthMode(); +} + +void SetBlendMode(const BPCmd &bp) +{ + g_renderer->SetBlendMode(false); +} + +void SetDitherMode(const BPCmd &bp) +{ + g_renderer->SetDitherMode(); +} + +void SetLogicOpMode(const BPCmd &bp) +{ + g_renderer->SetLogicOpMode(); +} + +void SetColorMask(const BPCmd &bp) +{ + g_renderer->SetColorMask(); +} + +void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const bool &fromZBuffer, const bool &isIntensityFmt, const u32 ©fmt, const int &scaleByHalf) +{ + if (!g_ActiveConfig.bEFBCopyDisable) + { +// if (g_ActiveConfig.bCopyEFBToTexture) +// { + g_texture_cache->CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, !!scaleByHalf, rc); +// } +// else +// { +// PanicAlert("TODO: Implement EFB copying to RAM %s %d\n", __FILE__, __LINE__); +// } + } +} + +void ClearScreen(const BPCmd &bp, const EFBRectangle &rc) +{ + bool colorEnable = bpmem.blendmode.colorupdate; + bool alphaEnable = (bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24 && bpmem.blendmode.alphaupdate); + bool zEnable = bpmem.zmode.updateenable; + + if (colorEnable || alphaEnable || zEnable) + { + u32 color = (bpmem.clearcolorAR << 16) | bpmem.clearcolorGB; + u32 z = bpmem.clearZValue; + + g_renderer->ClearScreen(rc, colorEnable, alphaEnable, zEnable, color, z); + } +} + +void RestoreRenderState(const BPCmd &bp) +{ + g_renderer->RestoreAPIState(); +} + +bool GetConfig(const int &type) +{ + switch (type) + { + case CONFIG_ISWII: + return g_VideoInitialize.bWii; + case CONFIG_DISABLEFOG: + return g_ActiveConfig.bDisableFog; + case CONFIG_SHOWEFBREGIONS: + return false; + default: + PanicAlert("GetConfig Error: Unknown Config Type!"); + return false; + } +} + +u8 *GetPointer(const u32 &address) +{ + return g_VideoInitialize.pGetMemoryPointer(address); +} + +void SetTextureMode(const BPCmd &bp) +{ + g_renderer->SetSamplerState(bp.address & 3, (bp.address & 0xE0) == 0xA0); +} + +void SetInterlacingMode(const BPCmd &bp) +{ + // TODO +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBase.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBase.cpp new file mode 100644 index 0000000000..4d83237ec4 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBase.cpp @@ -0,0 +1,338 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Common +#include "StringUtil.h" + +// VideoCommon +#include "VideoConfig.h" +#include "XFStructs.h" + +// DX11 +#include "DX11_D3DBase.h" +#include "DX11_D3DTexture.h" +#include "DX11_D3DShader.h" +#include "DX11_Render.h" + +#include + +#pragma comment(lib, "dxguid.lib") +#pragma comment(lib, "d3d11.lib") +#pragma comment(lib, "dxgi.lib") + +namespace DX11 +{ + +HINSTANCE hD3DXDll = NULL; +D3DX11COMPILEFROMMEMORYTYPE PD3DX11CompileFromMemory = NULL; +D3DX11FILTERTEXTURETYPE PD3DX11FilterTexture = NULL; +D3DX11SAVETEXTURETOFILEATYPE PD3DX11SaveTextureToFileA = NULL; +D3DX11SAVETEXTURETOFILEWTYPE PD3DX11SaveTextureToFileW = NULL; + +namespace D3D +{ + +ID3D11Device* device = NULL; +ID3D11DeviceContext* context = NULL; +IDXGISwapChain* swapchain = NULL; +D3D_FEATURE_LEVEL featlevel; +D3DTexture2D* backbuf = NULL; +HWND hWnd; + +bool bgra_textures_supported; + +#define NUM_SUPPORTED_FEATURE_LEVELS 3 +const D3D_FEATURE_LEVEL supported_feature_levels[NUM_SUPPORTED_FEATURE_LEVELS] = { + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0 +}; + +unsigned int xres, yres; + +bool bFrameInProgress = false; + +HRESULT Create(HWND wnd) +{ + hWnd = wnd; + HRESULT hr; + + RECT client; + GetClientRect(hWnd, &client); + xres = client.right - client.left; + yres = client.bottom - client.top; + + // try to load D3DX11 first to check whether we have proper runtime support + // try to use the dll the plugin was compiled against first - don't bother about debug runtimes + hD3DXDll = LoadLibraryA(StringFromFormat("d3dx11_%d.dll", D3DX11_SDK_VERSION).c_str()); + if (!hD3DXDll) + { + // if that fails, use the dll which should be available in every SDK which officially supports DX11. + hD3DXDll = LoadLibraryA("d3dx11_42.dll"); + if (!hD3DXDll) + { + MessageBoxA(NULL, "Failed to load d3dx11_42.dll, update your DX11 runtime, please", "Critical error", MB_OK | MB_ICONERROR); + return E_FAIL; + } + else + { + NOTICE_LOG(VIDEO, "Successfully loaded d3dx11_42.dll. If you're having trouble, try updating your DX runtime first."); + } + } + else + { + NOTICE_LOG(VIDEO, "Successfully loaded %s.", StringFromFormat("d3dx11_%d.dll", D3DX11_SDK_VERSION).c_str()); + } + + PD3DX11CompileFromMemory = (D3DX11COMPILEFROMMEMORYTYPE)GetProcAddress(hD3DXDll, "D3DX11CompileFromMemory"); + if (PD3DX11CompileFromMemory == NULL) MessageBoxA(NULL, "GetProcAddress failed for D3DX11CompileFromMemory!", "Critical error", MB_OK | MB_ICONERROR); + + PD3DX11FilterTexture = (D3DX11FILTERTEXTURETYPE)GetProcAddress(hD3DXDll, "D3DX11FilterTexture"); + if (PD3DX11FilterTexture == NULL) MessageBoxA(NULL, "GetProcAddress failed for D3DX11FilterTexture!", "Critical error", MB_OK | MB_ICONERROR); + + PD3DX11SaveTextureToFileA = (D3DX11SAVETEXTURETOFILEATYPE)GetProcAddress(hD3DXDll, "D3DX11SaveTextureToFileA"); + if (PD3DX11SaveTextureToFileA == NULL) MessageBoxA(NULL, "GetProcAddress failed for D3DX11SaveTextureToFileA!", "Critical error", MB_OK | MB_ICONERROR); + + PD3DX11SaveTextureToFileW = (D3DX11SAVETEXTURETOFILEWTYPE)GetProcAddress(hD3DXDll, "D3DX11SaveTextureToFileW"); + if (PD3DX11SaveTextureToFileW == NULL) MessageBoxA(NULL, "GetProcAddress failed for D3DX11SaveTextureToFileW!", "Critical error", MB_OK | MB_ICONERROR); + + // D3DX11 is fine, initialize D3D11 + IDXGIFactory* factory; + IDXGIAdapter* adapter; + IDXGIOutput* output; + hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory); + if (FAILED(hr)) MessageBox(wnd, _T("Failed to create IDXGIFactory object"), _T("Dolphin Direct3D 11 plugin"), MB_OK | MB_ICONERROR); + + hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter); + if (FAILED(hr)) + { + // try using the first one + hr = factory->EnumAdapters(0, &adapter); + if (FAILED(hr)) MessageBox(wnd, _T("Failed to enumerate adapters"), _T("Dolphin Direct3D 11 plugin"), MB_OK | MB_ICONERROR); + } + + // TODO: Make this configurable + hr = adapter->EnumOutputs(0, &output); + if (FAILED(hr)) + { + // try using the first one + hr = adapter->EnumOutputs(0, &output); + if (FAILED(hr)) MessageBox(wnd, _T("Failed to enumerate outputs"), _T("Dolphin Direct3D 11 plugin"), MB_OK | MB_ICONERROR); + } + + // this will need to be changed once multisampling gets implemented + DXGI_SWAP_CHAIN_DESC swap_chain_desc; + memset(&swap_chain_desc, 0, sizeof(swap_chain_desc)); + swap_chain_desc.BufferCount = 1; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.OutputWindow = wnd; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.Windowed = TRUE; + + DXGI_MODE_DESC mode_desc; + memset(&mode_desc, 0, sizeof(mode_desc)); + mode_desc.Width = xres; + mode_desc.Height = yres; + mode_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + mode_desc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + hr = output->FindClosestMatchingMode(&mode_desc, &swap_chain_desc.BufferDesc, NULL); + if (FAILED(hr)) + MessageBox(wnd, _T("Failed to find a supported video mode"), _T("Dolphin Direct3D 11 plugin"), MB_OK | MB_ICONERROR); + + // forcing buffer resolution to xres and yres.. TODO: The new video mode might not actually be supported! + swap_chain_desc.BufferDesc.Width = xres; + swap_chain_desc.BufferDesc.Height = yres; + +#if defined(_DEBUG) || defined(DEBUGFAST) + D3D11_CREATE_DEVICE_FLAG device_flags = (D3D11_CREATE_DEVICE_FLAG)(D3D11_CREATE_DEVICE_DEBUG|D3D11_CREATE_DEVICE_SINGLETHREADED); +#else + D3D11_CREATE_DEVICE_FLAG device_flags = D3D11_CREATE_DEVICE_SINGLETHREADED; +#endif + hr = D3D11CreateDeviceAndSwapChain(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, device_flags, + supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS, + D3D11_SDK_VERSION, &swap_chain_desc, &swapchain, &device, + &featlevel, &context); + if (FAILED(hr) || !device || !context || !swapchain) + { + MessageBox(wnd, _T("Failed to initialize Direct3D.\nMake sure your video card supports at least D3D 10.0"), _T("Dolphin Direct3D 11 plugin"), MB_OK | MB_ICONERROR); + SAFE_RELEASE(device); + SAFE_RELEASE(context); + SAFE_RELEASE(swapchain); + return E_FAIL; + } + SetDebugObjectName((ID3D11DeviceChild*)context, "device context"); + SAFE_RELEASE(factory); + SAFE_RELEASE(output); + SAFE_RELEASE(adapter); + + ID3D11Texture2D* buf; + hr = swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf); + if (FAILED(hr)) + { + MessageBox(wnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 plugin"), MB_OK | MB_ICONERROR); + SAFE_RELEASE(device); + SAFE_RELEASE(context); + SAFE_RELEASE(swapchain); + return E_FAIL; + } + backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET); + SAFE_RELEASE(buf); + CHECK(backbuf!=NULL, "Create back buffer texture"); + SetDebugObjectName((ID3D11DeviceChild*)backbuf->GetTex(), "backbuffer texture"); + SetDebugObjectName((ID3D11DeviceChild*)backbuf->GetRTV(), "backbuffer render target view"); + + context->OMSetRenderTargets(1, &backbuf->GetRTV(), NULL); + + // BGRA textures are easier to deal with in TextureCache, but might not be supported by the hardware + UINT format_support; + device->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM, &format_support); + bgra_textures_supported = (format_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0; + + gfxstate = new EmuGfxState; + stateman = new StateManager; + return S_OK; +} + +void Close() +{ + // unload D3DX11 + FreeLibrary(hD3DXDll); + PD3DX11FilterTexture = NULL; + PD3DX11SaveTextureToFileA = NULL; + PD3DX11SaveTextureToFileW = NULL; + + // release all bound resources + context->ClearState(); + SAFE_RELEASE(backbuf); + SAFE_RELEASE(swapchain); + SAFE_DELETE(gfxstate); + SAFE_DELETE(stateman); + context->Flush(); // immediately destroy device objects + + SAFE_RELEASE(context); + ULONG references = device->Release(); + if (references) + { + ERROR_LOG(VIDEO, "Unreleased references: %i.", references); + } + else + { + NOTICE_LOG(VIDEO, "Successfully released all device references!"); + } + device = NULL; +} + +/* just returning the 4_0 ones here */ +const char* VertexShaderVersionString() { return "vs_4_0"; } +const char* PixelShaderVersionString() { return "ps_4_0"; } + +D3DTexture2D* &GetBackBuffer() { return backbuf; } +unsigned int GetBackBufferWidth() { return xres; } +unsigned int GetBackBufferHeight() { return yres; } + +bool BGRATexturesSupported() { return bgra_textures_supported; } + +// Returns the maximum width/height of a texture. This value only depends upon the feature level in DX11 +unsigned int GetMaxTextureSize() +{ + switch (featlevel) + { + case D3D_FEATURE_LEVEL_11_0: + return 16384; + break; + + case D3D_FEATURE_LEVEL_10_1: + case D3D_FEATURE_LEVEL_10_0: + return 8192; + break; + + case D3D_FEATURE_LEVEL_9_3: + return 4096; + break; + + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + return 2048; + break; + + default: + return 0; + break; + } +} + +void Reset() +{ + // release all back buffer references + SAFE_RELEASE(backbuf); + + // resize swapchain buffers + RECT client; + GetClientRect(hWnd, &client); + xres = client.right - client.left; + yres = client.bottom - client.top; + D3D::swapchain->ResizeBuffers(1, xres, yres, DXGI_FORMAT_R8G8B8A8_UNORM, 0); + + // recreate back buffer texture + ID3D11Texture2D* buf; + HRESULT hr = swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf); + if (FAILED(hr)) + { + MessageBox(hWnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 plugin"), MB_OK | MB_ICONERROR); + SAFE_RELEASE(device); + SAFE_RELEASE(context); + SAFE_RELEASE(swapchain); + return; + } + backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET); + SAFE_RELEASE(buf); + CHECK(backbuf!=NULL, "Create back buffer texture"); + SetDebugObjectName((ID3D11DeviceChild*)backbuf->GetTex(), "backbuffer texture"); + SetDebugObjectName((ID3D11DeviceChild*)backbuf->GetRTV(), "backbuffer render target view"); +} + +bool BeginFrame() +{ + if (bFrameInProgress) + { + PanicAlert("BeginFrame called although a frame is already in progress"); + return false; + } + bFrameInProgress = true; + return (device != NULL); +} + +void EndFrame() +{ + if (!bFrameInProgress) + { + PanicAlert("EndFrame called although no frame is in progress"); + return; + } + bFrameInProgress = false; +} + +void Present() +{ + // TODO: Is 1 the correct value for vsyncing? + swapchain->Present((UINT)g_ActiveConfig.bVSync, 0); +} + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBase.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBase.h new file mode 100644 index 0000000000..8e86a55b8e --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBase.h @@ -0,0 +1,96 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +// Common +#include "Common.h" + +// DX11 +#include +#include "DX11_GfxState.h" +#include "DX11_D3DBlob.h" + +namespace DX11 +{ + +#define SAFE_RELEASE(x) { if (x) (x)->Release(); (x) = NULL; } +#define SAFE_DELETE(x) { delete (x); (x) = NULL; } +#define SAFE_DELETE_ARRAY(x) { delete[] (x); (x) = NULL; } +#define CHECK(cond, Message, ...) if (!(cond)) { PanicAlert(__FUNCTION__ "Failed in %s at line %d: " Message, __FILE__, __LINE__, __VA_ARGS__); } + +class D3DTexture2D; +namespace D3D +{ + +HRESULT Create(HWND wnd); +void Close(); + +extern ID3D11Device* device; +extern ID3D11DeviceContext* context; +extern IDXGISwapChain* swapchain; +extern bool bFrameInProgress; + +void Reset(); +bool BeginFrame(); +void EndFrame(); +void Present(); + +unsigned int GetBackBufferWidth(); +unsigned int GetBackBufferHeight(); +D3DTexture2D* &GetBackBuffer(); +const char* PixelShaderVersionString(); +const char* VertexShaderVersionString(); +bool BGRATexturesSupported(); + +unsigned int GetMaxTextureSize(); + +// Ihis function will assign a name to the given resource. +// The DirectX debug layer will make it easier to identify resources that way, +// e.g. when listing up all resources who have unreleased references. +inline void SetDebugObjectName(ID3D11DeviceChild* resource, const char* name) +{ +#if defined(_DEBUG) || defined(DEBUGFAST) + resource->SetPrivateData( WKPDID_D3DDebugObjectName, (UINT)strlen(name), name); +#endif +} + +} // namespace + + +// Used to not require the SDK and runtime versions to match: +// Linking with d3dx11.lib makes the most recent d3dx11_xx.dll of the +// compiler's SDK a requirement, but this plugin works with DX11 runtimes +// back to August 2009 even if the plugin was built with June 2010. +// Add any d3dx11 functions which you want to use here and load them in Create() +typedef HRESULT (WINAPI* D3DX11COMPILEFROMMEMORYTYPE)(LPCSTR, SIZE_T, LPCSTR, const D3D10_SHADER_MACRO*, LPD3D10INCLUDE, LPCSTR, LPCSTR, UINT, UINT, ID3DX11ThreadPump*, ID3D10Blob**, ID3D10Blob**, HRESULT*); +typedef HRESULT (WINAPI* D3DX11FILTERTEXTURETYPE)(ID3D11DeviceContext*, ID3D11Resource*, UINT, UINT); +typedef HRESULT (WINAPI* D3DX11SAVETEXTURETOFILEATYPE)(ID3D11DeviceContext*, ID3D11Resource*, D3DX11_IMAGE_FILE_FORMAT, LPCSTR); +typedef HRESULT (WINAPI* D3DX11SAVETEXTURETOFILEWTYPE)(ID3D11DeviceContext*, ID3D11Resource*, D3DX11_IMAGE_FILE_FORMAT, LPCWSTR); + +extern D3DX11COMPILEFROMMEMORYTYPE PD3DX11CompileFromMemory; +extern D3DX11FILTERTEXTURETYPE PD3DX11FilterTexture; +extern D3DX11SAVETEXTURETOFILEATYPE PD3DX11SaveTextureToFileA; +extern D3DX11SAVETEXTURETOFILEWTYPE PD3DX11SaveTextureToFileW; + +#ifdef UNICODE +#define PD3DX11SaveTextureToFile PD3DX11SaveTextureToFileW +#else +#define PD3DX11SaveTextureToFile PD3DX11SaveTextureToFileA +#endif + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBlob.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBlob.cpp new file mode 100644 index 0000000000..553bb76435 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBlob.cpp @@ -0,0 +1,68 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "DX11_D3DBlob.h" + +namespace DX11 +{ + +D3DBlob::D3DBlob(unsigned int blob_size, const u8* init_data) : ref(1), size(blob_size), blob(NULL) +{ + data = new u8[blob_size]; + if (init_data) memcpy(data, init_data, size); +} + +D3DBlob::D3DBlob(ID3D10Blob* d3dblob) : ref(1) +{ + blob = d3dblob; + data = (u8*)blob->GetBufferPointer(); + size = blob->GetBufferSize(); + d3dblob->AddRef(); +} + +D3DBlob::~D3DBlob() +{ + if (blob) blob->Release(); + else delete[] data; +} + +void D3DBlob::AddRef() +{ + ++ref; +} + +unsigned int D3DBlob::Release() +{ + if (--ref == 0) + { + delete this; + return 0; + } + return ref; +} + +unsigned int D3DBlob::Size() +{ + return size; +} + +u8* D3DBlob::Data() +{ + return data; +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBlob.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBlob.h new file mode 100644 index 0000000000..01bbc38f17 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DBlob.h @@ -0,0 +1,52 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "Common.h" +#include + +namespace DX11 +{ + +// use this class instead ID3D10Blob or ID3D11Blob whenever possible +class D3DBlob +{ +public: + // memory will be copied into an own buffer + D3DBlob(unsigned int blob_size, const u8* init_data = NULL); + + // d3dblob will be AddRef'd + D3DBlob(ID3D10Blob* d3dblob); + + void AddRef(); + unsigned int Release(); + + unsigned int Size(); + u8* Data(); + +private: + ~D3DBlob(); + + unsigned int ref; + unsigned int size; + + u8* data; + ID3D10Blob* blob; +}; + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DShader.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DShader.cpp new file mode 100644 index 0000000000..9523ed72a6 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DShader.cpp @@ -0,0 +1,150 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include + +#include "VideoConfig.h" +#include "DX11_D3DShader.h" + +namespace DX11 +{ + +namespace D3D +{ + +// bytecode->shader +ID3D11VertexShader* CreateVertexShaderFromByteCode(void* bytecode, unsigned int len) +{ + ID3D11VertexShader* v_shader; + HRESULT hr = D3D::device->CreateVertexShader(bytecode, len, NULL, &v_shader); + if (FAILED(hr)) + { + PanicAlert("CreateVertexShaderFromByteCode failed from %p (size %d) at %s %d\n", bytecode, len, __FILE__, __LINE__); + v_shader = NULL; + } + return v_shader; +} + +// code->bytecode +bool CompileVertexShader(const char* code, unsigned int len, D3DBlob** blob) +{ + ID3D10Blob* shaderBuffer = NULL; + ID3D10Blob* errorBuffer = NULL; + +#if defined(_DEBUG) || defined(DEBUGFAST) + UINT flags = D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY|D3D10_SHADER_DEBUG|D3D10_SHADER_WARNINGS_ARE_ERRORS; +#else + UINT flags = D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY|D3D10_SHADER_OPTIMIZATION_LEVEL3|D3D10_SHADER_SKIP_VALIDATION; +#endif + HRESULT hr = PD3DX11CompileFromMemory(code, len, NULL, NULL, NULL, "main", D3D::VertexShaderVersionString(), + flags, 0, NULL, &shaderBuffer, &errorBuffer, NULL); + + if (FAILED(hr) || errorBuffer) + { + std::string msg = (char*)errorBuffer->GetBufferPointer(); + msg += "\n\n"; + msg += code; + MessageBoxA(0, msg.c_str(), "Error compiling pixel shader", MB_ICONERROR); + + *blob = NULL; + errorBuffer->Release(); + } + else + { + *blob = new D3DBlob(shaderBuffer); + shaderBuffer->Release(); + } + return SUCCEEDED(hr); +} + +// bytecode->shader +ID3D11PixelShader* CreatePixelShaderFromByteCode(void* bytecode, unsigned int len) +{ + ID3D11PixelShader* p_shader; + HRESULT hr = D3D::device->CreatePixelShader(bytecode, len, NULL, &p_shader); + if (FAILED(hr)) + { + PanicAlert("CreatePixelShaderFromByteCode failed at %s %d\n", __FILE__, __LINE__); + p_shader = NULL; + } + return p_shader; +} + +// code->bytecode +bool CompilePixelShader(const char* code, unsigned int len, D3DBlob** blob) +{ + ID3D10Blob* shaderBuffer = NULL; + ID3D10Blob* errorBuffer = NULL; + +#if defined(_DEBUG) || defined(DEBUGFAST) + UINT flags = D3D10_SHADER_DEBUG|D3D10_SHADER_WARNINGS_ARE_ERRORS; +#else + UINT flags = D3D10_SHADER_OPTIMIZATION_LEVEL3; +#endif + HRESULT hr = PD3DX11CompileFromMemory(code, len, NULL, NULL, NULL, "main", D3D::PixelShaderVersionString(), + flags, 0, NULL, &shaderBuffer, &errorBuffer, NULL); + + if (FAILED(hr) || errorBuffer) + { + std::string msg = (char*)errorBuffer->GetBufferPointer(); + msg += "\n\n"; + msg += code; + MessageBoxA(0, msg.c_str(), "Error compiling pixel shader", MB_ICONERROR); + + *blob = NULL; + errorBuffer->Release(); + } + else + { + *blob = new D3DBlob(shaderBuffer); + shaderBuffer->Release(); + } + return SUCCEEDED(hr); +} + +ID3D11VertexShader* CompileAndCreateVertexShader(const char* code, unsigned int len) +{ + D3DBlob* blob = NULL; + if (CompileVertexShader(code, len, &blob)) + { + ID3D11VertexShader* v_shader = CreateVertexShaderFromByteCode(blob); + blob->Release(); + return v_shader; + } + PanicAlert("Failed to compile and create vertex shader from %p (size %d) at %s %d\n", code, len, __FILE__, __LINE__); + return NULL; +} + +ID3D11PixelShader* CompileAndCreatePixelShader(const char* code, unsigned int len) +{ + D3DBlob* blob = NULL; + CompilePixelShader(code, len, &blob); + if (blob) + { + ID3D11PixelShader* p_shader = CreatePixelShaderFromByteCode(blob); + blob->Release(); + return p_shader; + } + PanicAlert("Failed to compile and create pixel shader, %s %d\n", __FILE__, __LINE__); + return NULL; +} + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DShader.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DShader.h new file mode 100644 index 0000000000..ee34c3a4a0 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DShader.h @@ -0,0 +1,44 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "DX11_D3DBase.h" + +namespace DX11 +{ + +namespace D3D +{ + ID3D11VertexShader* CreateVertexShaderFromByteCode(void* bytecode, unsigned int len); + ID3D11PixelShader* CreatePixelShaderFromByteCode(void* bytecode, unsigned int len); + + // The returned bytecode buffers should be Release()d. + bool CompileVertexShader(const char* code, unsigned int len, D3DBlob** blob); + bool CompilePixelShader(const char* code, unsigned int len, D3DBlob** blob); + + // Utility functions + ID3D11VertexShader* CompileAndCreateVertexShader(const char* code, unsigned int len); + ID3D11PixelShader* CompileAndCreatePixelShader(const char* code, unsigned int len); + + inline ID3D11VertexShader* CreateVertexShaderFromByteCode(D3DBlob* bytecode) { return CreateVertexShaderFromByteCode(bytecode->Data(), bytecode->Size()); } + inline ID3D11PixelShader* CreatePixelShaderFromByteCode(D3DBlob* bytecode) { return CreatePixelShaderFromByteCode(bytecode->Data(), bytecode->Size()); } + inline ID3D11VertexShader* CompileAndCreateVertexShader(D3DBlob* code) { return CompileAndCreateVertexShader((const char*)code->Data(), code->Size()); } + inline ID3D11PixelShader* CompileAndCreatePixelShader(D3DBlob* code) { return CompileAndCreatePixelShader((const char*)code->Data(), code->Size()); } +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DTexture.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DTexture.cpp new file mode 100644 index 0000000000..8fda3a23ea --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DTexture.cpp @@ -0,0 +1,125 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include "DX11_D3DBase.h" +#include "DX11_D3DTexture.h" + +namespace DX11 +{ + +namespace D3D +{ + +void ReplaceRGBATexture2D(ID3D11Texture2D* pTexture, const u8* buffer, unsigned int width, unsigned int height, unsigned int pitch, unsigned int level, D3D11_USAGE usage) +{ + if (usage == D3D11_USAGE_DYNAMIC) + { + D3D11_MAPPED_SUBRESOURCE map; + HRESULT hr = D3D::context->Map(pTexture, level, D3D11_MAP_WRITE_DISCARD, 0, &map); + CHECK(SUCCEEDED(hr), "ID3D11DeviceContext::Map failed! (%x)", hr); + if (4 * pitch == map.RowPitch) + { + memcpy(map.pData, buffer, map.RowPitch * height); + } + else + { + for (unsigned int y = 0; y < height; ++y) + memcpy((u8*)map.pData + y * map.RowPitch, (u32*)buffer + y * pitch, map.RowPitch); + } + D3D::context->Unmap(pTexture, level); + } + else + { + D3D11_BOX dest_region = CD3D11_BOX(0, 0, 0, width, height, 1); + D3D::context->UpdateSubresource(pTexture, level, &dest_region, buffer, 4*pitch, 4*pitch*height); + } +} + +} // namespace + +D3DTexture2D* D3DTexture2D::Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT fmt, unsigned int levels) +{ + ID3D11Texture2D* pTexture = NULL; + HRESULT hr; + + D3D11_CPU_ACCESS_FLAG cpuflags; + if (usage == D3D11_USAGE_STAGING) + cpuflags = (D3D11_CPU_ACCESS_FLAG)((int)D3D11_CPU_ACCESS_WRITE|(int)D3D11_CPU_ACCESS_READ); + else if (usage == D3D11_USAGE_DYNAMIC) + cpuflags = D3D11_CPU_ACCESS_WRITE; + else + cpuflags = (D3D11_CPU_ACCESS_FLAG)0; + + D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(fmt, width, height, 1, levels, bind, usage, cpuflags); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &pTexture); + if (FAILED(hr)) + { + PanicAlert("Failed to create texture at %s, line %d: hr=%#x\n", __FILE__, __LINE__, hr); + return NULL; + } + + D3DTexture2D* ret = new D3DTexture2D(pTexture, bind); + SAFE_RELEASE(pTexture); + return ret; +} + +void D3DTexture2D::AddRef() +{ + ++ref; +} + +UINT D3DTexture2D::Release() +{ + --ref; + if (ref == 0) + { + delete this; + return 0; + } + return ref; +} + +D3DTexture2D::D3DTexture2D(ID3D11Texture2D* texptr, D3D11_BIND_FLAG bind, + DXGI_FORMAT srv_format, DXGI_FORMAT dsv_format, DXGI_FORMAT rtv_format) + : ref(1), tex(texptr), srv(NULL), rtv(NULL), dsv(NULL) +{ + D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = CD3D11_SHADER_RESOURCE_VIEW_DESC(D3D11_SRV_DIMENSION_TEXTURE2D, srv_format); + D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc = CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D, dsv_format); + D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = CD3D11_RENDER_TARGET_VIEW_DESC(D3D11_RTV_DIMENSION_TEXTURE2D, rtv_format); + + if (bind & D3D11_BIND_SHADER_RESOURCE) + D3D::device->CreateShaderResourceView(tex, &srv_desc, &srv); + + if (bind & D3D11_BIND_RENDER_TARGET) + D3D::device->CreateRenderTargetView(tex, &rtv_desc, &rtv); + + if (bind & D3D11_BIND_DEPTH_STENCIL) + D3D::device->CreateDepthStencilView(tex, &dsv_desc, &dsv); + + tex->AddRef(); +} + +D3DTexture2D::~D3DTexture2D() +{ + SAFE_RELEASE(srv); + SAFE_RELEASE(rtv); + SAFE_RELEASE(dsv); + SAFE_RELEASE(tex); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DTexture.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DTexture.h new file mode 100644 index 0000000000..722848da2a --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DTexture.h @@ -0,0 +1,60 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "DX11_D3DBase.h" + +namespace DX11 +{ + +namespace D3D +{ + void ReplaceRGBATexture2D(ID3D11Texture2D* pTexture, const u8* buffer, unsigned int width, unsigned int height, unsigned int pitch, unsigned int level, D3D11_USAGE usage); +} + +class D3DTexture2D +{ +public: + // there are two ways to create a D3DTexture2D object: + // either create an ID3D11Texture2D object, pass it to the constructor and specify what views to create + // or let the texture automatically be created by D3DTexture2D::Create + + D3DTexture2D(ID3D11Texture2D* texptr, D3D11_BIND_FLAG bind, DXGI_FORMAT srv_format = DXGI_FORMAT_UNKNOWN, DXGI_FORMAT dsv_format = DXGI_FORMAT_UNKNOWN, DXGI_FORMAT rtv_format = DXGI_FORMAT_UNKNOWN); + static D3DTexture2D* Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT, unsigned int levels = 1); + + // reference counting, use AddRef() when creating a new reference and Release() it when you don't need it anymore + void AddRef(); + UINT Release(); + + ID3D11Texture2D* &GetTex() { return tex; } + ID3D11ShaderResourceView* &GetSRV() { return srv; } + ID3D11RenderTargetView* &GetRTV() { return rtv; } + ID3D11DepthStencilView* &GetDSV() { return dsv; } + +private: + ~D3DTexture2D(); + + ID3D11Texture2D* tex; + ID3D11ShaderResourceView* srv; + ID3D11RenderTargetView* rtv; + ID3D11DepthStencilView* dsv; + D3D11_BIND_FLAG bindflags; + UINT ref; +}; + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DUtil.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DUtil.cpp new file mode 100644 index 0000000000..42987652e5 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DUtil.cpp @@ -0,0 +1,651 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Common +#include "Common.h" + +// DX11 +#include "DX11_Render.h" +#include "DX11_D3DBase.h" +#include "DX11_D3DUtil.h" +#include "DX11_D3DTexture.h" +#include "DX11_PixelShaderCache.h" +#include "DX11_VertexShaderCache.h" +#include "DX11_D3DShader.h" + +namespace DX11 +{ + +namespace D3D +{ + +CD3DFont font; + +#define MAX_NUM_VERTICES 300 +struct FONT2DVERTEX { + float x,y,z; + float col[4]; + float tu, tv; +}; + +inline FONT2DVERTEX InitFont2DVertex(float x, float y, u32 color, float tu, float tv) +{ + FONT2DVERTEX v; v.x=x; v.y=y; v.z=0; v.tu = tu; v.tv = tv; + v.col[0] = ((float)((color >> 16) & 0xFF)) / 255.f; + v.col[1] = ((float)((color >> 8) & 0xFF)) / 255.f; + v.col[2] = ((float)((color >> 0) & 0xFF)) / 255.f; + v.col[3] = ((float)((color >> 24) & 0xFF)) / 255.f; + return v; +} + +CD3DFont::CD3DFont() : m_dwTexWidth(512), m_dwTexHeight(512) +{ + m_pTexture = NULL; + m_pVB = NULL; + m_InputLayout = NULL; + m_pshader = NULL; + m_vshader = NULL; +} + +const char fontpixshader[] = { + "Texture2D tex2D;\n" + "SamplerState linearSampler\n" + "{\n" + " Filter = MIN_MAG_MIP_LINEAR;\n" + " AddressU = D3D11_TEXTURE_ADDRESS_BORDER;\n" + " AddressV = D3D11_TEXTURE_ADDRESS_BORDER;\n" + " BorderColor = float4(0.f, 0.f, 0.f, 0.f);\n" + "};\n" + "struct PS_INPUT\n" + "{\n" + " float4 pos : SV_POSITION;\n" + " float4 col : COLOR;\n" + " float2 tex : TEXCOORD;\n" + "};\n" + "float4 main( PS_INPUT input ) : SV_Target\n" + "{\n" + " return tex2D.Sample( linearSampler, input.tex ) * input.col;\n" + "};\n" +}; + +const char fontvertshader[] = { + "struct VS_INPUT\n" + "{\n" + " float4 pos : POSITION;\n" + " float4 col : COLOR;\n" + " float2 tex : TEXCOORD;\n" + "};\n" + "struct PS_INPUT\n" + "{\n" + " float4 pos : SV_POSITION;\n" + " float4 col : COLOR;\n" + " float2 tex : TEXCOORD;\n" + "};\n" + "PS_INPUT main( VS_INPUT input )\n" + "{\n" + " PS_INPUT output;\n" + " output.pos = input.pos;\n" + " output.col = input.col;\n" + " output.tex = input.tex;\n" + " return output;\n" + "};\n" +}; + +int CD3DFont::Init() +{ + HRESULT hr; + + // prepare to create a bitmap + unsigned int* pBitmapBits; + BITMAPINFO bmi; + ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = (int)m_dwTexWidth; + bmi.bmiHeader.biHeight = -(int)m_dwTexHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biBitCount = 32; + + // create a DC and a bitmap for the font + HDC hDC = CreateCompatibleDC(NULL); + HBITMAP hbmBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void**)&pBitmapBits, NULL, 0); + SetMapMode(hDC, MM_TEXT); + + // create a GDI font + HFONT hFont = CreateFont(24, 0, 0, 0, FW_NORMAL, FALSE, + FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, PROOF_QUALITY, + VARIABLE_PITCH, _T("Tahoma")); + if (NULL == hFont) return E_FAIL; + + HGDIOBJ hOldbmBitmap = SelectObject(hDC, hbmBitmap); + HGDIOBJ hOldFont = SelectObject(hDC, hFont); + + // set text properties + SetTextColor(hDC, 0xFFFFFF); + SetBkColor (hDC, 0); + SetTextAlign(hDC, TA_TOP); + + TEXTMETRICW tm; + GetTextMetricsW(hDC, &tm); + m_LineHeight = tm.tmHeight; + + // loop through all printable characters and output them to the bitmap + // meanwhile, keep track of the corresponding tex coords for each character. + int x = 0, y = 0; + char str[2] = "\0"; + for (int c = 0; c < 127 - 32; c++) + { + str[0] = c + 32; + SIZE size; + GetTextExtentPoint32A(hDC, str, 1, &size); + if ((int)(x+size.cx+1) > m_dwTexWidth) + { + x = 0; + y += m_LineHeight; + } + + ExtTextOutA(hDC, x+1, y+0, ETO_OPAQUE | ETO_CLIPPED, NULL, str, 1, NULL); + m_fTexCoords[c][0] = (float) x /m_dwTexWidth; + m_fTexCoords[c][1] = (float) y /m_dwTexHeight; + m_fTexCoords[c][2] = (float)(x+size.cx)/m_dwTexWidth; + m_fTexCoords[c][3] = (float)(y+size.cy)/m_dwTexHeight; + + x += size.cx + 3; // 3 to work around annoying ij conflict (part of the j ends up with the i) + } + + // create a new texture for the font + // possible optimization: store the converted data in a buffer and fill the texture on creation. + // That way, we can use a static texture + ID3D11Texture2D* buftex; + D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, m_dwTexWidth, m_dwTexHeight, + 1, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DYNAMIC, + D3D11_CPU_ACCESS_WRITE); + hr = device->CreateTexture2D(&texdesc, NULL, &buftex); + if (FAILED(hr)) + { + PanicAlert("Failed to create font texture"); + return hr; + } + D3D::SetDebugObjectName((ID3D11DeviceChild*)buftex, "texture of a CD3DFont object"); + + // lock the surface and write the alpha values for the set pixels + D3D11_MAPPED_SUBRESOURCE texmap; + hr = context->Map(buftex, 0, D3D11_MAP_WRITE_DISCARD, 0, &texmap); + if (FAILED(hr)) PanicAlert("Failed to map a texture at %s %d\n", __FILE__, __LINE__); + + for (y = 0; y < m_dwTexHeight; y++) + { + u32* pDst32 = (u32*)((u8*)texmap.pData + y * texmap.RowPitch); + for (x = 0; x < m_dwTexWidth; x++) + { + const u8 bAlpha = (pBitmapBits[m_dwTexWidth * y + x] & 0xff); + *pDst32++ = (((bAlpha << 4) | bAlpha) << 24) | 0xFFFFFF; + } + } + + // clean up + context->Unmap(buftex, 0); + hr = D3D::device->CreateShaderResourceView(buftex, NULL, &m_pTexture); + if (FAILED(hr)) PanicAlert("Failed to create shader resource view at %s %d\n", __FILE__, __LINE__); + SAFE_RELEASE(buftex); + + SelectObject(hDC, hOldbmBitmap); + DeleteObject(hbmBitmap); + + SelectObject(hDC, hOldFont); + DeleteObject(hFont); + + // setup device objects for drawing + m_pshader = D3D::CompileAndCreatePixelShader(fontpixshader, sizeof(fontpixshader)); + if (m_pshader == NULL) PanicAlert("Failed to create pixel shader, %s %d\n", __FILE__, __LINE__); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_pshader, "pixel shader of a CD3DFont object"); + + D3DBlob* vsbytecode; + D3D::CompileVertexShader(fontvertshader, sizeof(fontvertshader), &vsbytecode); + if (vsbytecode == NULL) PanicAlert("Failed to compile vertex shader, %s %d\n", __FILE__, __LINE__); + m_vshader = D3D::CreateVertexShaderFromByteCode(vsbytecode); + if (m_vshader == NULL) PanicAlert("Failed to create vertex shader, %s %d\n", __FILE__, __LINE__); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_vshader, "vertex shader of a CD3DFont object"); + + const D3D11_INPUT_ELEMENT_DESC desc[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + hr = D3D::device->CreateInputLayout(desc, 3, vsbytecode->Data(), vsbytecode->Size(), &m_InputLayout); + if (FAILED(hr)) PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__); + SAFE_RELEASE(vsbytecode); + + D3D11_BLEND_DESC blenddesc; + blenddesc.AlphaToCoverageEnable = FALSE; + blenddesc.IndependentBlendEnable = FALSE; + blenddesc.RenderTarget[0].BlendEnable = TRUE; + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; + blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + hr = D3D::device->CreateBlendState(&blenddesc, &m_blendstate); + CHECK(hr==S_OK, "Create font blend state"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_blendstate, "blend state of a CD3DFont object"); + + // this might need to be changed when adding multisampling support + D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0.f, false, false, false, false); + hr = D3D::device->CreateRasterizerState(&rastdesc, &m_raststate); + CHECK(hr==S_OK, "Create font rasterizer state"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_raststate, "rasterizer state of a CD3DFont object"); + + D3D11_BUFFER_DESC vbdesc = CD3D11_BUFFER_DESC(MAX_NUM_VERTICES*sizeof(FONT2DVERTEX), D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + if (FAILED(hr = device->CreateBuffer(&vbdesc, NULL, &m_pVB))) + { + PanicAlert("Failed to create font vertex buffer at %s, line %d\n", __FILE__, __LINE__); + return hr; + } + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_pVB, "vertex buffer of a CD3DFont object"); + return S_OK; +} + +int CD3DFont::Shutdown() +{ + SAFE_RELEASE(m_pVB); + SAFE_RELEASE(m_pTexture); + SAFE_RELEASE(m_InputLayout); + SAFE_RELEASE(m_pshader); + SAFE_RELEASE(m_vshader); + + SAFE_RELEASE(m_blendstate); + SAFE_RELEASE(m_raststate); + + return S_OK; +} + +int CD3DFont::DrawTextScaled(float x, float y, float size, float spacing, u32 dwColor, const char* strText, bool center) +{ + if (!m_pVB) return 0; + + UINT stride = sizeof(FONT2DVERTEX); + UINT bufoffset = 0; + + float scalex = 1 / (float)D3D::GetBackBufferWidth() * 2.f; + float scaley = 1 / (float)D3D::GetBackBufferHeight() * 2.f; + float sizeratio = size / (float)m_LineHeight; + + // translate starting positions + float sx = x * scalex - 1.f; + float sy = 1.f - y * scaley; + char c; + + // fill vertex buffer + FONT2DVERTEX* pVertices; + int dwNumTriangles = 0L; + + D3D11_MAPPED_SUBRESOURCE vbmap; + HRESULT hr = context->Map(m_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vbmap); + if (FAILED(hr)) PanicAlert("Mapping vertex buffer failed, %s %d\n", __FILE__, __LINE__); + pVertices = (D3D::FONT2DVERTEX*)vbmap.pData; + + // if center was requested, set current position as centre + // this is currently never used + if (center) + { + const char *oldText = strText; + float mx=0; + float maxx=0; + + while (c = *strText++) + { + if (c == ('\n')) mx = 0; + if (c < (' ') ) continue; + c -= 32; + mx += (m_fTexCoords[c][2]-m_fTexCoords[c][0])/(m_fTexCoords[0][3] - m_fTexCoords[0][1]) + spacing; + if (mx > maxx) maxx = mx; + } + sx -= scalex*maxx*size; + strText = oldText; + } + // set general pipeline state + D3D::stateman->PushBlendState(m_blendstate); + D3D::stateman->PushRasterizerState(m_raststate); + D3D::stateman->Apply(); + + D3D::context->PSSetShader(m_pshader, NULL, 0); + D3D::context->VSSetShader(m_vshader, NULL, 0); + + D3D::context->IASetInputLayout(m_InputLayout); + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + D3D::context->PSSetShaderResources(0, 1, &m_pTexture); + + float fStartX = sx; + while (c = *strText++) + { + if (c == ('\n')) + { + sx = fStartX; + sy -= scaley * size; + } + if (c < (' ')) continue; + + c -= 32; + float tx1 = m_fTexCoords[c][0]; + float ty1 = m_fTexCoords[c][1]; + float tx2 = m_fTexCoords[c][2]; + float ty2 = m_fTexCoords[c][3]; + + float w = (float)(tx2-tx1) * m_dwTexWidth * scalex * sizeratio; + float h = (float)(ty1-ty2) * m_dwTexHeight * scaley * sizeratio; + + FONT2DVERTEX v[6]; + v[0] = InitFont2DVertex( sx, h+sy, dwColor, tx1, ty2); + v[1] = InitFont2DVertex( sx, sy, dwColor, tx1, ty1); + v[2] = InitFont2DVertex(w+sx, h+sy, dwColor, tx2, ty2); + v[3] = InitFont2DVertex(w+sx, sy, dwColor, tx2, ty1); + v[4] = v[2]; + v[5] = v[1]; + + memcpy(pVertices, v, 6*sizeof(FONT2DVERTEX)); + + pVertices+=6; + dwNumTriangles += 2; + + if (dwNumTriangles * 3 > (MAX_NUM_VERTICES - 6)) + { + context->Unmap(m_pVB, 0); + + D3D::context->IASetVertexBuffers(0, 1, &m_pVB, &stride, &bufoffset); + D3D::context->Draw(3 * dwNumTriangles, 0); + + dwNumTriangles = 0; + D3D11_MAPPED_SUBRESOURCE vbmap; + hr = context->Map(m_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vbmap); + if (FAILED(hr)) PanicAlert("Mapping vertex buffer failed, %s %d\n", __FILE__, __LINE__); + pVertices = (D3D::FONT2DVERTEX*)vbmap.pData; + } + sx += w + spacing * scalex * size; + } + + // Unlock and render the vertex buffer + context->Unmap(m_pVB, 0); + if (dwNumTriangles > 0) + { + D3D::context->IASetVertexBuffers(0, 1, &m_pVB, &stride, &bufoffset); + D3D::context->Draw(3 * dwNumTriangles, 0); + } + D3D::stateman->PopBlendState(); + D3D::stateman->PopRasterizerState(); + return S_OK; +} + +ID3D11Buffer* CreateQuadVertexBuffer(unsigned int size, void* data) +{ + ID3D11Buffer* vb; + D3D11_BUFFER_DESC vbdesc; + vbdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + vbdesc.ByteWidth = size; + vbdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + vbdesc.MiscFlags = 0; + vbdesc.Usage = D3D11_USAGE_DYNAMIC; + if (data) + { + D3D11_SUBRESOURCE_DATA bufdata; + bufdata.pSysMem = data; + if (FAILED(device->CreateBuffer(&vbdesc, &bufdata, &vb))) return NULL; + } + else if (FAILED(device->CreateBuffer(&vbdesc, NULL, &vb))) return NULL; + + return vb; +} + +ID3D11SamplerState* linear_copy_sampler = NULL; +ID3D11SamplerState* point_copy_sampler = NULL; +ID3D11Buffer* stqvb = NULL; +ID3D11Buffer* stsqvb = NULL; +ID3D11Buffer* clearvb = NULL; + +typedef struct { float x,y,z,u,v; } STQVertex; +typedef struct { float x,y,z,u,v; } STSQVertex; +typedef struct { float x,y,z; u32 col; } ClearVertex; + +struct +{ + float u1, v1, u2, v2; +} tex_quad_data; + +struct +{ + MathUtil::Rectangle rdest; + float u1, v1, u2, v2; +} tex_sub_quad_data; + +struct +{ + u32 col; + float z; +} clear_quad_data; + +void InitUtils() +{ + float border[4] = { 0.f, 0.f, 0.f, 0.f }; + D3D11_SAMPLER_DESC samDesc = CD3D11_SAMPLER_DESC(D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_TEXTURE_ADDRESS_BORDER, D3D11_TEXTURE_ADDRESS_BORDER, D3D11_TEXTURE_ADDRESS_BORDER, 0.f, 1, D3D11_COMPARISON_ALWAYS, border, -D3D11_FLOAT32_MAX, D3D11_FLOAT32_MAX); + HRESULT hr = D3D::device->CreateSamplerState(&samDesc, &point_copy_sampler); + if (FAILED(hr)) PanicAlert("Failed to create sampler state at %s %d\n", __FILE__, __LINE__); + else SetDebugObjectName((ID3D11DeviceChild*)point_copy_sampler, "point copy sampler state"); + + samDesc = CD3D11_SAMPLER_DESC(D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_BORDER, D3D11_TEXTURE_ADDRESS_BORDER, D3D11_TEXTURE_ADDRESS_BORDER, 0.f, 1, D3D11_COMPARISON_ALWAYS, border, -D3D11_FLOAT32_MAX, D3D11_FLOAT32_MAX); + hr = D3D::device->CreateSamplerState(&samDesc, &linear_copy_sampler); + if (FAILED(hr)) PanicAlert("Failed to create sampler state at %s %d\n", __FILE__, __LINE__); + else SetDebugObjectName((ID3D11DeviceChild*)linear_copy_sampler, "linear copy sampler state"); + + // cached data used to avoid unnecessarily reloading the vertex buffers + memset(&tex_quad_data, 0, sizeof(tex_quad_data)); + memset(&tex_sub_quad_data, 0, sizeof(tex_sub_quad_data)); + memset(&clear_quad_data, 0, sizeof(clear_quad_data)); + + STQVertex stqcoords[4] = { + {-1.0f, 1.0f, 0.0f, 0, 0}, + { 1.0f, 1.0f, 0.0f, 0, 0}, + {-1.0f,-1.0f, 0.0f, 0, 0}, + { 1.0f,-1.0f, 0.0f, 0, 0}, + }; + + STSQVertex stsqcoords[4]; + memset(stsqcoords, 0, sizeof(stsqcoords)); + + ClearVertex cqcoords[4] = { + {-1.0f, 1.0f, 0, 0}, + { 1.0f, 1.0f, 0, 0}, + {-1.0f, -1.0f, 0, 0}, + { 1.0f, -1.0f, 0, 0}, + }; + + stqvb = CreateQuadVertexBuffer(4*sizeof(STQVertex), stqcoords); + CHECK(stqvb!=NULL, "Create vertex buffer of drawShadedTexQuad"); + SetDebugObjectName((ID3D11DeviceChild*)stqvb, "vertex buffer of drawShadedTexQuad"); + + stsqvb = CreateQuadVertexBuffer(4*sizeof(STSQVertex), stsqcoords); + CHECK(stsqvb!=NULL, "Create vertex buffer of drawShadedTexSubQuad"); + SetDebugObjectName((ID3D11DeviceChild*)stsqvb, "vertex buffer of drawShadedTexSubQuad"); + + clearvb = CreateQuadVertexBuffer(4*sizeof(ClearVertex), cqcoords); + CHECK(clearvb!=NULL, "Create vertex buffer of drawClearQuad"); + SetDebugObjectName((ID3D11DeviceChild*)clearvb, "vertex buffer of drawClearQuad"); + + font.Init(); +} + +void ShutdownUtils() +{ + font.Shutdown(); + SAFE_RELEASE(point_copy_sampler); + SAFE_RELEASE(linear_copy_sampler); + SAFE_RELEASE(stqvb); + SAFE_RELEASE(stsqvb); + SAFE_RELEASE(clearvb); +} + +void SetPointCopySampler() +{ + D3D::context->PSSetSamplers(0, 1, &point_copy_sampler); +} + +void SetLinearCopySampler() +{ + D3D::context->PSSetSamplers(0, 1, &linear_copy_sampler); +} + +void drawShadedTexQuad(ID3D11ShaderResourceView* texture, + const D3D11_RECT* rSource, + int SourceWidth, + int SourceHeight, + ID3D11PixelShader* PShader, + ID3D11VertexShader* Vshader, + ID3D11InputLayout* layout) +{ + float sw = 1.0f /(float) SourceWidth; + float sh = 1.0f /(float) SourceHeight; + float u1 = ((float)rSource->left) * sw; + float u2 = ((float)rSource->right) * sw; + float v1 = ((float)rSource->top) * sh; + float v2 = ((float)rSource->bottom) * sh; + + STQVertex coords[4] = { + {-1.0f, 1.0f, 0.0f, u1, v1}, + { 1.0f, 1.0f, 0.0f, u2, v1}, + {-1.0f,-1.0f, 0.0f, u1, v2}, + { 1.0f,-1.0f, 0.0f, u2, v2}, + }; + + // only upload the data to VRAM if it changed + if (tex_quad_data.u1 != u1 || tex_quad_data.v1 != v1 || + tex_quad_data.u2 != u2 || tex_quad_data.v2 != v2) + { + D3D11_MAPPED_SUBRESOURCE map; + D3D::context->Map(stqvb, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, coords, sizeof(coords)); + D3D::context->Unmap(stqvb, 0); + tex_quad_data.u1 = u1; + tex_quad_data.v1 = v1; + tex_quad_data.u2 = u2; + tex_quad_data.v2 = v2; + } + UINT stride = sizeof(STQVertex); + UINT offset = 0; + + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + D3D::context->IASetInputLayout(layout); + D3D::context->IASetVertexBuffers(0, 1, &stqvb, &stride, &offset); + D3D::context->PSSetShader(PShader, NULL, 0); + D3D::context->PSSetShaderResources(0, 1, &texture); + D3D::context->VSSetShader(Vshader, NULL, 0); + D3D::stateman->Apply(); + D3D::context->Draw(4, 0); + + ID3D11ShaderResourceView* texres = NULL; + context->PSSetShaderResources(0, 1, &texres); // immediately unbind the texture +} + +void drawShadedTexSubQuad(ID3D11ShaderResourceView* texture, + const MathUtil::Rectangle* rSource, + int SourceWidth, + int SourceHeight, + const MathUtil::Rectangle* rDest, + ID3D11PixelShader* PShader, + ID3D11VertexShader* Vshader, + ID3D11InputLayout* layout) +{ + float sw = 1.0f /(float) SourceWidth; + float sh = 1.0f /(float) SourceHeight; + float u1 = (rSource->left ) * sw; + float u2 = (rSource->right ) * sw; + float v1 = (rSource->top ) * sh; + float v2 = (rSource->bottom) * sh; + + STSQVertex coords[4] = { + { rDest->left , rDest->bottom, 0.0f, u1, v1}, + { rDest->right, rDest->bottom, 0.0f, u2, v1}, + { rDest->left , rDest->top , 0.0f, u1, v2}, + { rDest->right, rDest->top , 0.0f, u2, v2}, + }; + + // only upload the data to VRAM if it changed + if (memcmp(rDest, &tex_sub_quad_data.rdest, sizeof(rDest)) != 0 || + tex_sub_quad_data.u1 != u1 || tex_sub_quad_data.v1 != v1 || + tex_sub_quad_data.u2 != u2 || tex_sub_quad_data.v2 != v2) + { + D3D11_MAPPED_SUBRESOURCE map; + D3D::context->Map(stsqvb, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, coords, sizeof(coords)); + D3D::context->Unmap(stsqvb, 0); + tex_sub_quad_data.u1 = u1; + tex_sub_quad_data.v1 = v1; + tex_sub_quad_data.u2 = u2; + tex_sub_quad_data.v2 = v2; + memcpy(&tex_sub_quad_data.rdest, &rDest, sizeof(rDest)); + } + UINT stride = sizeof(STSQVertex); + UINT offset = 0; + + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context->IASetVertexBuffers(0, 1, &stsqvb, &stride, &offset); + context->IASetInputLayout(layout); + context->PSSetShaderResources(0, 1, &texture); + context->PSSetShader(PShader, NULL, 0); + context->VSSetShader(Vshader, NULL, 0); + stateman->Apply(); + context->Draw(4, 0); + + ID3D11ShaderResourceView* texres = NULL; + context->PSSetShaderResources(0, 1, &texres); // immediately unbind the texture +} + +void drawClearQuad(u32 Color, float z, ID3D11PixelShader* PShader, ID3D11VertexShader* Vshader, ID3D11InputLayout* layout) +{ + if (clear_quad_data.col != Color || clear_quad_data.z != z) + { + const ClearVertex coords[4] = { + {-1.0f, 1.0f, z, Color}, + { 1.0f, 1.0f, z, Color}, + {-1.0f, -1.0f, z, Color}, + { 1.0f, -1.0f, z, Color}, + }; + + D3D11_MAPPED_SUBRESOURCE map; + context->Map(clearvb, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, coords, sizeof(coords)); + context->Unmap(clearvb, 0); + clear_quad_data.col = Color; + clear_quad_data.z = z; + } + context->VSSetShader(Vshader, NULL, 0); + context->PSSetShader(PShader, NULL, 0); + context->IASetInputLayout(layout); + + UINT stride = sizeof(ClearVertex); + UINT offset = 0; + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context->IASetVertexBuffers(0, 1, &clearvb, &stride, &offset); + stateman->Apply(); + context->Draw(4, 0); +} + + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DUtil.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DUtil.h new file mode 100644 index 0000000000..0e7ad0ac77 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_D3DUtil.h @@ -0,0 +1,91 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include +#include + +#include "DX11_D3DBase.h" + +namespace DX11 +{ + +namespace D3D +{ + // Font creation flags + #define D3DFONT_BOLD 0x0001 + #define D3DFONT_ITALIC 0x0002 + + // Font rendering flags + #define D3DFONT_CENTERED 0x0001 + + class CD3DFont + { + ID3D11ShaderResourceView* m_pTexture; + ID3D11Buffer* m_pVB; + ID3D11InputLayout* m_InputLayout; + ID3D11PixelShader* m_pshader; + ID3D11VertexShader* m_vshader; + ID3D11BlendState* m_blendstate; + ID3D11RasterizerState* m_raststate; + const int m_dwTexWidth; + const int m_dwTexHeight; + unsigned int m_LineHeight; + float m_fTexCoords[128-32][4]; + + public: + CD3DFont(); + // 2D text drawing function + // Initializing and destroying device-dependent objects + int Init(); + int Shutdown(); + int DrawTextScaled(float x, float y, + float size, + float spacing, u32 dwColor, + const char* strText, bool center=true); + }; + + extern CD3DFont font; + + void InitUtils(); + void ShutdownUtils(); + + void SetPointCopySampler(); + void SetLinearCopySampler(); + + void drawShadedTexQuad(ID3D11ShaderResourceView* texture, + const D3D11_RECT* rSource, + int SourceWidth, + int SourceHeight, + ID3D11PixelShader* PShader, + ID3D11VertexShader* VShader, + ID3D11InputLayout* layout); + void drawShadedTexSubQuad(ID3D11ShaderResourceView* texture, + const MathUtil::Rectangle* rSource, + int SourceWidth, + int SourceHeight, + const MathUtil::Rectangle* rDest, + ID3D11PixelShader* PShader, + ID3D11VertexShader* Vshader, + ID3D11InputLayout* layout); + void drawClearQuad(u32 Color, float z, ID3D11PixelShader* PShader, ID3D11VertexShader* Vshader, ID3D11InputLayout* layout); + void SaveRenderStates(); + void RestoreRenderStates(); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_FramebufferManager.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_FramebufferManager.cpp new file mode 100644 index 0000000000..5a4d6562e3 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_FramebufferManager.cpp @@ -0,0 +1,162 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// VideoCommon +#include "VideoConfig.h" + +// DX11 +#include "DX11_Render.h" +#include "DX11_D3DBase.h" +#include "DX11_D3DTexture.h" +#include "DX11_D3DUtil.h" +#include "DX11_FramebufferManager.h" +#include "DX11_PixelShaderCache.h" +#include "DX11_VertexShaderCache.h" + +#include "../Main.h" + +namespace DX11 +{ + +XFBSource FramebufferManager::m_realXFBSource; // used in real XFB mode + +FramebufferManager::EFB FramebufferManager::m_efb; + +D3DTexture2D* &FramebufferManager::GetEFBColorTexture() { return m_efb.color_tex; } +ID3D11Texture2D* &FramebufferManager::GetEFBColorStagingBuffer() { return m_efb.color_staging_buf; } + +D3DTexture2D* &FramebufferManager::GetEFBDepthTexture() { return m_efb.depth_tex; } +D3DTexture2D* &FramebufferManager::GetEFBDepthReadTexture() { return m_efb.depth_read_texture; } +ID3D11Texture2D* &FramebufferManager::GetEFBDepthStagingBuffer() { return m_efb.depth_staging_buf; } + +FramebufferManager::FramebufferManager() +{ + m_efb.color_tex = NULL; + m_efb.color_staging_buf = NULL; + m_efb.depth_tex = NULL; + m_efb.depth_staging_buf = NULL; + m_efb.depth_read_texture = NULL; + + m_realXFBSource.tex = NULL; + + unsigned int target_width = Renderer::GetFullTargetWidth(); + unsigned int target_height = Renderer::GetFullTargetHeight(); + ID3D11Texture2D* buf; + D3D11_TEXTURE2D_DESC texdesc; + HRESULT hr; + + // create framebuffer color texture + m_efb.color_tex = D3DTexture2D::Create(target_width, target_height, + (D3D11_BIND_FLAG)(D3D11_BIND_RENDER_TARGET|D3D11_BIND_SHADER_RESOURCE), + D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM); + CHECK(m_efb.color_tex, "create EFB color texture (size: %dx%d)", target_width, target_height); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.color_tex->GetTex(), "EFB color texture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.color_tex->GetRTV(), "EFB color texture render target view"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.color_tex->GetSRV(), "EFB color texture shader resource view"); + + // create a staging texture for Renderer::AccessEFB + texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, 1, 1, 0, + D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_WRITE|D3D11_CPU_ACCESS_READ); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &m_efb.color_staging_buf); + CHECK(SUCCEEDED(hr), "create EFB color staging buffer (hr=%#x)", hr); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.color_staging_buf, "EFB color staging texture (used for Renderer::AccessEFB)"); + + // EFB depth buffer + texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R24G8_TYPELESS, target_width, + target_height, 1, 1, D3D11_BIND_DEPTH_STENCIL|D3D11_BIND_SHADER_RESOURCE); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &buf); + CHECK(hr==S_OK, "create EFB depth texture (size: %dx%d; hr=%#x)", target_width, target_height, hr); + m_efb.depth_tex = new D3DTexture2D(buf, (D3D11_BIND_FLAG)(D3D11_BIND_DEPTH_STENCIL|D3D11_BIND_SHADER_RESOURCE), + DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_D24_UNORM_S8_UINT); + SAFE_RELEASE(buf); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_tex->GetTex(), "EFB depth texture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_tex->GetDSV(), "EFB depth texture depth stencil view"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_tex->GetSRV(), "EFB depth texture shader resource view"); + + // render target for depth buffer access in Renderer::AccessEFB + texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R32_FLOAT, 4, 4, 1, 1, D3D11_BIND_RENDER_TARGET); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &buf); + CHECK(hr==S_OK, "create EFB depth read texture (hr=%#x)", hr); + m_efb.depth_read_texture = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET); + SAFE_RELEASE(buf); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_read_texture->GetTex(), "EFB depth read texture (used in Renderer::AccessEFB)"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_read_texture->GetRTV(), "EFB depth read texture render target view (used in Renderer::AccessEFB)"); + + // staging texture to which we copy the data from m_efb.depth_read_texture + texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R32_FLOAT, 4, 4, 1, 1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ|D3D11_CPU_ACCESS_WRITE); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &m_efb.depth_staging_buf); + CHECK(hr==S_OK, "create EFB depth staging buffer (hr=%#x)", hr); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_staging_buf, "EFB depth staging texture (used for Renderer::AccessEFB)"); +} + +FramebufferManager::~FramebufferManager() +{ + SAFE_RELEASE(m_efb.color_tex); + SAFE_RELEASE(m_efb.color_staging_buf); + SAFE_RELEASE(m_efb.depth_tex); + SAFE_RELEASE(m_efb.depth_staging_buf); + SAFE_RELEASE(m_efb.depth_read_texture); + + SAFE_RELEASE(m_realXFBSource.tex); +} + +void XFBSource::CopyEFB(const TargetRectangle& efbSource) +{ + // copy EFB data to XFB and restore render target again + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)texWidth, (float)texHeight); + D3D::context->RSSetViewports(1, &vp); + D3D::context->OMSetRenderTargets(1, &tex->GetRTV(), NULL); + D3D::SetLinearCopySampler(); + + D3DTexture2D* const ctex = FramebufferManager::GetEFBColorTexture(); + + D3D::drawShadedTexQuad(ctex->GetSRV(), efbSource.AsRECT(), + Renderer::GetFullTargetWidth(), Renderer::GetFullTargetHeight(), + PixelShaderCache::GetColorCopyProgram(), VertexShaderCache::GetSimpleVertexShader(), + VertexShaderCache::GetSimpleInputLayout()); + + D3D::context->OMSetRenderTargets(1, &ctex->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV()); +} + +XFBSource::~XFBSource() +{ + tex->Release(); +} + +XFBSourceBase* FramebufferManager::CreateXFBSource(unsigned int target_width, unsigned int target_height) +{ + XFBSource* const xfbs = new XFBSource; + xfbs->tex = D3DTexture2D::Create(target_width, target_height, + (D3D11_BIND_FLAG)(D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE), + D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM); + + return xfbs; +} + +void FramebufferManager::copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + // TODO + PanicAlert("copyToRealXFB not implemented, yet\n"); +} + +const XFBSource** FramebufferManager::getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount) +{ + PanicAlert("getRealXFBSource not implemented, yet\n"); + return NULL; +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_FramebufferManager.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_FramebufferManager.h new file mode 100644 index 0000000000..3a0179bdbd --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_FramebufferManager.h @@ -0,0 +1,108 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _DX11_FBMANAGER_D3D_H_ +#define _DX11_FBMANAGER_D3D_H_ + +#include + +#include "DX11_D3DBase.h" + +#include "../FramebufferManager.h" + +namespace DX11 +{ + +// On the GameCube, the game sends a request for the graphics processor to +// transfer its internal EFB (Embedded Framebuffer) to an area in GameCube RAM +// called the XFB (External Framebuffer). The size and location of the XFB is +// decided at the time of the copy, and the format is always YUYV. The video +// interface is given a pointer to the XFB, which will be decoded and +// displayed on the TV. +// +// There are two ways for Dolphin to emulate this: +// +// Real XFB mode: +// +// Dolphin will behave like the GameCube and encode the EFB to +// a portion of GameCube RAM. The emulated video interface will decode the data +// for output to the screen. +// +// Advantages: Behaves exactly like the GameCube. +// Disadvantages: Resolution will be limited. +// +// Virtual XFB mode: +// +// When a request is made to copy the EFB to an XFB, Dolphin +// will remember the RAM location and size of the XFB in a Virtual XFB list. +// The video interface will look up the XFB in the list and use the enhanced +// data stored there, if available. +// +// Advantages: Enables high resolution graphics, better than real hardware. +// Disadvantages: If the GameCube CPU writes directly to the XFB (which is +// possible but uncommon), the Virtual XFB will not capture this information. + +// There may be multiple XFBs in GameCube RAM. This is the maximum number to +// virtualize. + +struct XFBSource : public XFBSourceBase +{ + XFBSource() : tex(NULL) {} + ~XFBSource(); + + void CopyEFB(const TargetRectangle& efbSource); + + D3DTexture2D* tex; +}; + +class FramebufferManager : public ::FramebufferManagerBase +{ + friend struct XFBSource; + +public: + FramebufferManager(); + ~FramebufferManager(); + + static D3DTexture2D* &GetEFBColorTexture(); + static ID3D11Texture2D* &GetEFBColorStagingBuffer(); + + static D3DTexture2D* &GetEFBDepthTexture(); + static D3DTexture2D* &GetEFBDepthReadTexture(); + static ID3D11Texture2D* &GetEFBDepthStagingBuffer(); + + XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height); + +private: + static void copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + static const XFBSource** getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount); + + static XFBSource m_realXFBSource; // used in real XFB mode + + static struct EFB + { + D3DTexture2D* color_tex; + ID3D11Texture2D* color_staging_buf; + + D3DTexture2D* depth_tex; + ID3D11Texture2D* depth_staging_buf; + D3DTexture2D* depth_read_texture; + } m_efb; +}; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_GfxState.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_GfxState.cpp new file mode 100644 index 0000000000..79ed26815d --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_GfxState.cpp @@ -0,0 +1,372 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "VideoConfig.h" +#include "DX11_GfxState.h" + +namespace DX11 +{ + +namespace D3D +{ + +EmuGfxState* gfxstate; +StateManager* stateman; + +EmuGfxState::EmuGfxState() : vertexshader(NULL), vsbytecode(NULL), pixelshader(NULL), psbytecode(NULL), apply_called(false) +{ + for (unsigned int k = 0;k < 8;k++) + { + float border[4] = {0.f, 0.f, 0.f, 0.f}; + shader_resources[k] = NULL; + samplerdesc[k] = CD3D11_SAMPLER_DESC(D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0.f, 16, D3D11_COMPARISON_ALWAYS, border, -D3D11_FLOAT32_MAX, D3D11_FLOAT32_MAX); + if(g_ActiveConfig.iMaxAnisotropy > 1) samplerdesc[k].Filter = D3D11_FILTER_ANISOTROPIC; + } + + memset(&blenddesc, 0, sizeof(blenddesc)); + blenddesc.AlphaToCoverageEnable = FALSE; + blenddesc.IndependentBlendEnable = FALSE; + blenddesc.RenderTarget[0].BlendEnable = FALSE; + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + + memset(&depthdesc, 0, sizeof(depthdesc)); + depthdesc.DepthEnable = TRUE; + depthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + depthdesc.DepthFunc = D3D11_COMPARISON_LESS; + depthdesc.StencilEnable = FALSE; + depthdesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + depthdesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + + // this probably must be changed once multisampling support gets added + rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0, false, true, false, false); + + pscbuf = NULL; + vscbuf = NULL; + vshaderchanged = false; + inp_layout = NULL; + num_inp_elems = 0; + + pscbufchanged = false; + vscbufchanged = false; +} + +EmuGfxState::~EmuGfxState() +{ + for (unsigned int k = 0;k < 8;k++) + SAFE_RELEASE(shader_resources[k]) + + SAFE_RELEASE(vsbytecode); + SAFE_RELEASE(psbytecode); + SAFE_RELEASE(vertexshader); + SAFE_RELEASE(pixelshader); + + SAFE_RELEASE(pscbuf); + SAFE_RELEASE(vscbuf); + + SAFE_RELEASE(inp_layout); +} + +// TODO: No need to store the whole bytecode, signature might be enough (?) +void EmuGfxState::SetVShader(ID3D11VertexShader* shader, D3DBlob* bcode) +{ + // TODO: vshaderchanged actually just needs to be true if the signature changed + if (bcode && vsbytecode != bcode) vshaderchanged = true; + SAFE_RELEASE(vsbytecode); + SAFE_RELEASE(vertexshader); + + if (shader && bcode) + { + vertexshader = shader; + shader->AddRef(); + vsbytecode = bcode; + bcode->AddRef(); + } + else if (shader || bcode) + { + PanicAlert("Invalid parameters!\n"); + } +} + +void EmuGfxState::SetPShader(ID3D11PixelShader* shader) +{ + if (pixelshader) pixelshader->Release(); + pixelshader = shader; + if (shader) shader->AddRef(); +} + +void EmuGfxState::SetInputElements(const D3D11_INPUT_ELEMENT_DESC* elems, UINT num) +{ + num_inp_elems = num; + memcpy(inp_elems, elems, num*sizeof(D3D11_INPUT_ELEMENT_DESC)); +} + +void EmuGfxState::SetShaderResource(unsigned int stage, ID3D11ShaderResourceView* srv) +{ + if (shader_resources[stage]) + shader_resources[stage]->Release(); + shader_resources[stage] = srv; + if (srv) + srv->AddRef(); +} + +void EmuGfxState::ApplyState() +{ + HRESULT hr; + + // input layout (only needs to be updated if the vertex shader signature changed) + if (vshaderchanged) + { + SAFE_RELEASE(inp_layout); + hr = D3D::device->CreateInputLayout(inp_elems, num_inp_elems, vsbytecode->Data(), vsbytecode->Size(), &inp_layout); + if (FAILED(hr)) PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__); + SetDebugObjectName((ID3D11DeviceChild*)inp_layout, "an input layout of EmuGfxState"); + vshaderchanged = false; + } + D3D::context->IASetInputLayout(inp_layout); + + // vertex shader + // TODO: divide the global variables of the generated shaders into about 5 constant buffers + // TODO: improve interaction between EmuGfxState and global state management, so that we don't need to set the constant buffers every time + if (!vscbuf) + { + unsigned int size = ((sizeof(vsconstants))&(~0xf))+0x10; // must be a multiple of 16 + D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(size, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + hr = device->CreateBuffer(&cbdesc, NULL, &vscbuf); + CHECK(hr==S_OK, "Create vertex shader constant buffer (size=%u)", size); + SetDebugObjectName((ID3D11DeviceChild*)vscbuf, "a vertex shader constant buffer of EmuGfxState"); + } + if (vscbufchanged) + { + D3D11_MAPPED_SUBRESOURCE map; + context->Map(vscbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, vsconstants, sizeof(vsconstants)); + context->Unmap(vscbuf, 0); + } + D3D::context->VSSetConstantBuffers(0, 1, &vscbuf); + + // pixel shader + if (!pscbuf) + { + unsigned int size = ((sizeof(psconstants))&(~0xf))+0x10; // must be a multiple of 16 + D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(size, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + device->CreateBuffer(&cbdesc, NULL, &pscbuf); + CHECK(hr==S_OK, "Create pixel shader constant buffer (size=%u)", size); + SetDebugObjectName((ID3D11DeviceChild*)pscbuf, "a pixel shader constant buffer of EmuGfxState"); + } + if (pscbufchanged) + { + D3D11_MAPPED_SUBRESOURCE map; + context->Map(pscbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, psconstants, sizeof(psconstants)); + context->Unmap(pscbuf, 0); + pscbufchanged = false; + } + D3D::context->PSSetConstantBuffers(0, 1, &pscbuf); + + ID3D11SamplerState* samplerstate[8]; + for (unsigned int stage = 0; stage < 8; stage++) + { + if (shader_resources[stage]) + { + if(g_ActiveConfig.iMaxAnisotropy > 1) samplerdesc[stage].Filter = D3D11_FILTER_ANISOTROPIC; + hr = D3D::device->CreateSamplerState(&samplerdesc[stage], &samplerstate[stage]); + if (FAILED(hr)) PanicAlert("Fail %s %d, stage=%d\n", __FILE__, __LINE__, stage); + else SetDebugObjectName((ID3D11DeviceChild*)samplerstate[stage], "a sampler state of EmuGfxState"); + } + else samplerstate[stage] = NULL; + } + D3D::context->PSSetSamplers(0, 8, samplerstate); + for (unsigned int stage = 0; stage < 8; stage++) + SAFE_RELEASE(samplerstate[stage]); + + ID3D11BlendState* blstate; + hr = device->CreateBlendState(&blenddesc, &blstate); + if (FAILED(hr)) PanicAlert("Failed to create blend state at %s %d\n", __FILE__, __LINE__); + stateman->PushBlendState(blstate); + SetDebugObjectName((ID3D11DeviceChild*)blstate, "a blend state of EmuGfxState"); + SAFE_RELEASE(blstate); + + rastdesc.FillMode = (g_ActiveConfig.bWireFrame) ? D3D11_FILL_WIREFRAME : D3D11_FILL_SOLID; + ID3D11RasterizerState* raststate; + hr = device->CreateRasterizerState(&rastdesc, &raststate); + if (FAILED(hr)) PanicAlert("Failed to create rasterizer state at %s %d\n", __FILE__, __LINE__); + SetDebugObjectName((ID3D11DeviceChild*)raststate, "a rasterizer state of EmuGfxState"); + stateman->PushRasterizerState(raststate); + SAFE_RELEASE(raststate); + + ID3D11DepthStencilState* depth_state; + hr = device->CreateDepthStencilState(&depthdesc, &depth_state); + if (SUCCEEDED(hr)) SetDebugObjectName((ID3D11DeviceChild*)depth_state, "a depth-stencil state of EmuGfxState"); + else PanicAlert("Failed to create depth state at %s %d\n", __FILE__, __LINE__); + D3D::stateman->PushDepthState(depth_state); + SAFE_RELEASE(depth_state); + + context->PSSetShader(pixelshader, NULL, 0); + context->VSSetShader(vertexshader, NULL, 0); + context->PSSetShaderResources(0, 8, shader_resources); + + stateman->Apply(); + apply_called = true; +} + +void EmuGfxState::AlphaPass() +{ + if (!apply_called) ERROR_LOG(VIDEO, "EmuGfxState::AlphaPass called without having called ApplyState before!") + else stateman->PopBlendState(); + + // pixel shader for alpha pass is different, so update it + context->PSSetShader(pixelshader, NULL, 0); + + ID3D11BlendState* blstate; + D3D11_BLEND_DESC desc = blenddesc; + desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA; + desc.RenderTarget[0].BlendEnable = FALSE; + HRESULT hr = device->CreateBlendState(&desc, &blstate); + if (FAILED(hr)) PanicAlert("Failed to create blend state at %s %d\n", __FILE__, __LINE__); + SetDebugObjectName((ID3D11DeviceChild*)blstate, "a blend state of EmuGfxState (created during alpha pass)"); + stateman->PushBlendState(blstate); + blstate->Release(); + + stateman->Apply(); +} + +void EmuGfxState::Reset() +{ + for (unsigned int k = 0;k < 8;k++) + SAFE_RELEASE(shader_resources[k]); + + context->PSSetShaderResources(0, 8, shader_resources); // unbind all textures + if (apply_called) + { + stateman->PopBlendState(); + stateman->PopDepthState(); + stateman->PopRasterizerState(); + apply_called = false; + } +} + +void EmuGfxState::SetAlphaBlendEnable(bool enable) +{ + blenddesc.RenderTarget[0].BlendEnable = enable; +} + +void EmuGfxState::SetRenderTargetWriteMask(UINT8 mask) +{ + blenddesc.RenderTarget[0].RenderTargetWriteMask = mask; +} + +void EmuGfxState::SetSrcBlend(D3D11_BLEND val) +{ + // TODO: Check whether e.g. the dest color check is needed here + blenddesc.RenderTarget[0].SrcBlend = val; + if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; + else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_DEST_ALPHA; + else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA; + else blenddesc.RenderTarget[0].SrcBlendAlpha = val; +} + +void EmuGfxState::SetDestBlend(D3D11_BLEND val) +{ + // TODO: Check whether e.g. the source color check is needed here + blenddesc.RenderTarget[0].DestBlend = val; + if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_SRC_ALPHA; + else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_DEST_ALPHA; + else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA; + else blenddesc.RenderTarget[0].DestBlendAlpha = val; +} + +void EmuGfxState::SetBlendOp(D3D11_BLEND_OP val) +{ + blenddesc.RenderTarget[0].BlendOp = val; + blenddesc.RenderTarget[0].BlendOpAlpha = val; +} + +void EmuGfxState::SetSamplerFilter(DWORD stage, D3D11_FILTER filter) +{ + samplerdesc[stage].Filter = filter; +} + +template AutoState::AutoState(const T* object) : state(object) +{ + ((IUnknown*)state)->AddRef(); +} + +template AutoState::AutoState(const AutoState &source) +{ + state = source.GetPtr(); + ((T*)state)->AddRef(); +} + +template AutoState::~AutoState() +{ + if(state) ((T*)state)->Release(); + state = NULL; +} + +StateManager::StateManager() : cur_blendstate(NULL), cur_depthstate(NULL), cur_raststate(NULL) {} + +void StateManager::PushBlendState(const ID3D11BlendState* state) { blendstates.push(AutoBlendState(state)); } +void StateManager::PushDepthState(const ID3D11DepthStencilState* state) { depthstates.push(AutoDepthStencilState(state)); } +void StateManager::PushRasterizerState(const ID3D11RasterizerState* state) { raststates.push(AutoRasterizerState(state)); } +void StateManager::PopBlendState() { blendstates.pop(); } +void StateManager::PopDepthState() { depthstates.pop(); } +void StateManager::PopRasterizerState() { raststates.pop(); } + +void StateManager::Apply() +{ + if (!blendstates.empty()) + { + if (cur_blendstate != blendstates.top().GetPtr()) + { + cur_blendstate = (ID3D11BlendState*)blendstates.top().GetPtr(); + D3D::context->OMSetBlendState(cur_blendstate, NULL, 0xFFFFFFFF); + } + } + else ERROR_LOG(VIDEO, "Tried to apply without blend state!"); + + if (!depthstates.empty()) + { + if (cur_depthstate != depthstates.top().GetPtr()) + { + cur_depthstate = (ID3D11DepthStencilState*)depthstates.top().GetPtr(); + D3D::context->OMSetDepthStencilState(cur_depthstate, 0); + } + } + else ERROR_LOG(VIDEO, "Tried to apply without depth state!"); + + if (!raststates.empty()) + { + if (cur_raststate != raststates.top().GetPtr()) + { + cur_raststate = (ID3D11RasterizerState*)raststates.top().GetPtr(); + D3D::context->RSSetState(cur_raststate); + } + } + else ERROR_LOG(VIDEO, "Tried to apply without rasterizer state!"); +} + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_GfxState.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_GfxState.h new file mode 100644 index 0000000000..ff2423af79 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_GfxState.h @@ -0,0 +1,142 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include + +// VideoCommon +#include "VertexShaderGen.h" +#include "PixelShaderGen.h" + +// DX11 +#include "DX11_D3DBase.h" +#include "DX11_D3DBlob.h" + +namespace DX11 +{ + +namespace D3D +{ + +// stores the pipeline state to use when calling VertexManager::Flush() +class EmuGfxState +{ +public: + EmuGfxState(); + ~EmuGfxState(); + + void SetVShader(ID3D11VertexShader* shader, D3DBlob* bcode); + void SetPShader(ID3D11PixelShader* shader); + void SetInputElements(const D3D11_INPUT_ELEMENT_DESC* elems, UINT num); + void SetShaderResource(unsigned int stage, ID3D11ShaderResourceView* srv); + + void ApplyState(); // apply current state + void AlphaPass(); // only modify the current state to enable the alpha pass + void Reset(); + + // blend state + void SetAlphaBlendEnable(bool enable); + void SetRenderTargetWriteMask(UINT8 mask); + void SetSrcBlend(D3D11_BLEND val); + void SetDestBlend(D3D11_BLEND val); + void SetBlendOp(D3D11_BLEND_OP val); + + // sampler states + void SetSamplerFilter(DWORD stage, D3D11_FILTER filter); + + // TODO: add methods for changing the other states instead of modifying them directly + + D3D11_SAMPLER_DESC samplerdesc[8]; + D3D11_RASTERIZER_DESC rastdesc; + D3D11_DEPTH_STENCIL_DESC depthdesc; + + float psconstants[C_PENVCONST_END*4]; + float vsconstants[C_VENVCONST_END*4]; + bool vscbufchanged; + bool pscbufchanged; + +private: + ID3D11VertexShader* vertexshader; + D3DBlob* vsbytecode; + ID3D11PixelShader* pixelshader; + D3DBlob* psbytecode; + bool vshaderchanged; + + ID3D11Buffer* vscbuf; + ID3D11Buffer* pscbuf; + + ID3D11InputLayout* inp_layout; + D3D11_INPUT_ELEMENT_DESC inp_elems[32]; + int num_inp_elems; + + ID3D11ShaderResourceView* shader_resources[8]; + D3D11_BLEND_DESC blenddesc; + + bool apply_called; +}; + +template class AutoState +{ +public: + AutoState(const T* object); + AutoState(const AutoState &source); + ~AutoState(); + + const inline T* GetPtr() const { return state; } + +private: + const T* state; +}; + +typedef AutoState AutoBlendState; +typedef AutoState AutoDepthStencilState; +typedef AutoState AutoRasterizerState; + +class StateManager +{ +public: + StateManager(); + + // call any of these to change the affected states + void PushBlendState(const ID3D11BlendState* state); + void PushDepthState(const ID3D11DepthStencilState* state); + void PushRasterizerState(const ID3D11RasterizerState* state); + + // call these after drawing + void PopBlendState(); + void PopDepthState(); + void PopRasterizerState(); + + // call this before any drawing operation if states could have changed meanwhile + void Apply(); + +private: + std::stack blendstates; + std::stack depthstates; + std::stack raststates; + ID3D11BlendState* cur_blendstate; + ID3D11DepthStencilState* cur_depthstate; + ID3D11RasterizerState* cur_raststate; +}; + +extern EmuGfxState* gfxstate; +extern StateManager* stateman; + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_NativeVertexFormat.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_NativeVertexFormat.cpp new file mode 100644 index 0000000000..2ac5afb219 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_NativeVertexFormat.cpp @@ -0,0 +1,150 @@ + +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Common +#include "MemoryUtil.h" +#include "x64Emitter.h" +#include "ABI.h" + +// VideoCommon +#include "Profiler.h" +#include "CPMemory.h" +#include "VertexShaderGen.h" +#include "NativeVertexFormat.h" + +// DX11 +#include "DX11_D3DBase.h" +#include "DX11_VertexShaderCache.h" +#include "DX11_VertexManager.h" + +namespace DX11 +{ + +class D3DVertexFormat : public NativeVertexFormat +{ + D3D11_INPUT_ELEMENT_DESC m_elems[32]; + UINT m_num_elems; + +public: + D3DVertexFormat() : m_num_elems(0) {} + void Initialize(const PortableVertexDeclaration &_vtx_decl); + void SetupVertexPointers() const; +}; + +NativeVertexFormat* VertexManager::CreateNativeVertexFormat() +{ + return new D3DVertexFormat; +} + +DXGI_FORMAT VarToD3D(VarType t, int size) +{ + DXGI_FORMAT retval = DXGI_FORMAT_UNKNOWN; + static const DXGI_FORMAT lookup1[5] = { + DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R16_SNORM, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R32_FLOAT + }; + static const DXGI_FORMAT lookup2[5] = { + DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R16G16_SNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_R32G32_FLOAT + }; + static const DXGI_FORMAT lookup3[5] = { + DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32_FLOAT + }; + static const DXGI_FORMAT lookup4[5] = { + DXGI_FORMAT_R8G8B8A8_SNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R16G16B16A16_SNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R32G32B32A32_FLOAT + }; + + switch (size) + { + case 1: retval = lookup1[t]; break; + case 2: retval = lookup2[t]; break; + case 3: retval = lookup3[t]; break; + case 4: retval = lookup4[t]; break; + default: break; + } + if (retval == DXGI_FORMAT_UNKNOWN) + { + PanicAlert("VarToD3D: Invalid type/size combo %i , %i", (int)t, size); + } + return retval; +} + +void D3DVertexFormat::Initialize(const PortableVertexDeclaration &_vtx_decl) +{ + vertex_stride = _vtx_decl.stride; + memset(m_elems, 0, sizeof(m_elems)); + + m_elems[m_num_elems].SemanticName = "POSITION"; + m_elems[m_num_elems].AlignedByteOffset = 0; + m_elems[m_num_elems].Format = DXGI_FORMAT_R32G32B32_FLOAT; + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + + for (int i = 0; i < 3; i++) + { + if (_vtx_decl.normal_offset[i] > 0) + { + m_elems[m_num_elems].SemanticName = "NORMAL"; + m_elems[m_num_elems].SemanticIndex = i; + m_elems[m_num_elems].AlignedByteOffset = _vtx_decl.normal_offset[i]; + m_elems[m_num_elems].Format = VarToD3D(_vtx_decl.normal_gl_type, _vtx_decl.normal_gl_size); + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + } + } + + for (int i = 0; i < 2; i++) + { + if (_vtx_decl.color_offset[i] > 0) + { + m_elems[m_num_elems].SemanticName = "COLOR"; + m_elems[m_num_elems].SemanticIndex = i; + m_elems[m_num_elems].AlignedByteOffset = _vtx_decl.color_offset[i]; + m_elems[m_num_elems].Format = VarToD3D(_vtx_decl.color_gl_type, 4); + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + } + } + + for (int i = 0; i < 8; i++) + { + if (_vtx_decl.texcoord_offset[i] > 0) + { + m_elems[m_num_elems].SemanticName = "TEXCOORD"; + m_elems[m_num_elems].SemanticIndex = i; + m_elems[m_num_elems].AlignedByteOffset = _vtx_decl.texcoord_offset[i]; + m_elems[m_num_elems].Format = VarToD3D(_vtx_decl.texcoord_gl_type[i], _vtx_decl.texcoord_size[i]); + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + } + } + + if (_vtx_decl.posmtx_offset != -1) + { + m_elems[m_num_elems].SemanticName = "BLENDINDICES"; + m_elems[m_num_elems].AlignedByteOffset = _vtx_decl.posmtx_offset; + m_elems[m_num_elems].Format = DXGI_FORMAT_R8G8B8A8_UNORM; + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + } +} + +void D3DVertexFormat::SetupVertexPointers() const +{ + D3D::gfxstate->SetInputElements(m_elems, m_num_elems); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_PixelShaderCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_PixelShaderCache.cpp new file mode 100644 index 0000000000..9e89025ad2 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_PixelShaderCache.cpp @@ -0,0 +1,296 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Common +#include "Common.h" +#include "FileUtil.h" +#include "LinearDiskCache.h" + +// VideoCommon +#include "Statistics.h" +#include "VideoConfig.h" +#include "VertexLoader.h" +#include "BPMemory.h" +#include "XFMemory.h" +#include "ImageWrite.h" +#include "PixelShaderGen.h" +#include "PixelShaderManager.h" + +// DX11 +#include "DX11_D3DBase.h" +#include "DX11_D3DShader.h" +#include "DX11_PixelShaderCache.h" + +#include + +#include "../Main.h" + +namespace DX11 +{ + +PixelShaderCache::PSCache PixelShaderCache::PixelShaders; +const PixelShaderCache::PSCacheEntry* PixelShaderCache::last_entry; + +LinearDiskCache g_ps_disk_cache; + +ID3D11PixelShader* s_ColorMatrixProgram = NULL; +ID3D11PixelShader* s_ColorCopyProgram = NULL; +ID3D11PixelShader* s_DepthMatrixProgram = NULL; +ID3D11PixelShader* s_ClearProgram = NULL; + +const char clear_program_code[] = { + "void main(\n" + "out float4 ocol0 : SV_Target,\n" + "in float4 pos : SV_Position,\n" + "in float4 incol0 : COLOR0){\n" + "ocol0 = incol0;\n" + "}\n" +}; + +const char color_copy_program_code[] = { + "sampler samp0 : register(s0);\n" + "Texture2D Tex0 : register(t0);\n" + "void main(\n" + "out float4 ocol0 : SV_Target,\n" + "in float4 pos : SV_Position,\n" + "in float2 uv0 : TEXCOORD0){\n" + "ocol0 = Tex0.Sample(samp0,uv0);\n" + "}\n" +}; + +const char color_matrix_program_code[] = { + "sampler samp0 : register(s0);\n" + "Texture2D Tex0 : register(t0);\n" + "uniform float4 cColMatrix[5] : register(c0);\n" + "void main(\n" + "out float4 ocol0 : SV_Target,\n" + "in float4 pos : SV_Position,\n" + " in float2 uv0 : TEXCOORD0){\n" + "float4 texcol = Tex0.Sample(samp0,uv0);\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n" +}; + +const char depth_matrix_program[] = { + "sampler samp0 : register(s0);\n" + "Texture2D Tex0 : register(t0);\n" + "uniform float4 cColMatrix[5] : register(c0);\n" + "void main(\n" + "out float4 ocol0 : SV_Target,\n" + " in float4 pos : SV_Position,\n" + " in float2 uv0 : TEXCOORD0){\n" + "float4 texcol = Tex0.Sample(samp0,uv0);\n" + "float4 EncodedDepth = frac((texcol.r * (16777215.0f/16777216.0f)) * float4(1.0f,255.0f,255.0f*255.0f,255.0f*255.0f*255.0f));\n" + "texcol = float4((EncodedDepth.rgb * (16777216.0f/16777215.0f)),1.0f);\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n" +}; + +ID3D11PixelShader* PixelShaderCache::GetColorMatrixProgram() +{ + return s_ColorMatrixProgram; +} + +ID3D11PixelShader* PixelShaderCache::GetDepthMatrixProgram() +{ + return s_DepthMatrixProgram; +} + +ID3D11PixelShader* PixelShaderCache::GetColorCopyProgram() +{ + return s_ColorCopyProgram; +} + +ID3D11PixelShader* PixelShaderCache::GetClearProgram() +{ + return s_ClearProgram; +} + +// HACK to avoid some invasive VideoCommon changes +// these values are hardcoded, they depend on internal D3DCompile behavior; TODO: Solve this with D3DReflect or something +// offset given in floats, table index is float4 +unsigned int ps_constant_offset_table[] = { + 0, 4, 8, 12, // C_COLORS, 16 + 16, 20, 24, 28, // C_KCOLORS, 16 + 32, // C_ALPHA, 4 + 36, 40, 44, 48, 52, 56, 60, 64, // C_TEXDIMS, 32 + 68, 72, // C_ZBIAS, 8 + 76, 80, // C_INDTEXSCALE, 8 + 84, 88, 92, 96, 100, 104, // C_INDTEXMTX, 24 + 108, 112, // C_FOG, 8 +}; +void PixelShaderCache::SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + D3D::gfxstate->psconstants[ps_constant_offset_table[const_number] ] = f1; + D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]+1] = f2; + D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]+2] = f3; + D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]+3] = f4; + D3D::gfxstate->pscbufchanged = true; +} + +void PixelShaderCache::SetPSConstant4fv(unsigned int const_number, const float* f) +{ + memcpy(&D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]], f, sizeof(float)*4); + D3D::gfxstate->pscbufchanged = true; +} + +void PixelShaderCache::SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float* f) +{ + memcpy(&D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]], f, sizeof(float)*4*count); + D3D::gfxstate->pscbufchanged = true; +} + +// this class will load the precompiled shaders into our cache +class PixelShaderCacheInserter : public LinearDiskCacheReader +{ +public: + void Read(const u8* key, int key_size, const u8* value, int value_size) + { + PIXELSHADERUID uid; + if (key_size != sizeof(uid)) { + ERROR_LOG(VIDEO, "Wrong key size in pixel shader cache"); + return; + } + memcpy(&uid, key, key_size); + PixelShaderCache::InsertByteCode(uid, (void*)value, value_size); + } +}; + +PixelShaderCache::PixelShaderCache() +{ + // used when drawing clear quads + s_ClearProgram = D3D::CompileAndCreatePixelShader(clear_program_code, sizeof(clear_program_code)); + CHECK(s_ClearProgram!=NULL, "Create clear pixel shader"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)s_ClearProgram, "clear pixel shader"); + + // used when copying/resolving the color buffer + s_ColorCopyProgram = D3D::CompileAndCreatePixelShader(color_copy_program_code, sizeof(color_copy_program_code)); + CHECK(s_ColorCopyProgram!=NULL, "Create color copy pixel shader"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)s_ClearProgram, "color copy pixel shader"); + + // used for color conversion + s_ColorMatrixProgram = D3D::CompileAndCreatePixelShader(color_matrix_program_code, sizeof(color_matrix_program_code)); + CHECK(s_ColorMatrixProgram!=NULL, "Create color matrix pixel shader"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)s_ClearProgram, "color matrix pixel shader"); + + // used for depth copy + s_DepthMatrixProgram = D3D::CompileAndCreatePixelShader(depth_matrix_program, sizeof(depth_matrix_program)); + CHECK(s_DepthMatrixProgram!=NULL, "Create depth matrix pixel shader"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)s_ClearProgram, "depth matrix pixel shader"); + + Clear(); + + if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) + File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); + + char cache_filename[MAX_PATH]; + sprintf(cache_filename, "%sdx11-%s-ps.cache", File::GetUserPath(D_SHADERCACHE_IDX), g_globals->unique_id); + PixelShaderCacheInserter inserter; + g_ps_disk_cache.OpenAndRead(cache_filename, &inserter); +} + +// ONLY to be used during shutdown. +void PixelShaderCache::Clear() +{ + for (PSCache::iterator iter = PixelShaders.begin(); iter != PixelShaders.end(); iter++) + iter->second.Destroy(); + PixelShaders.clear(); +} + +PixelShaderCache::~PixelShaderCache() +{ + SAFE_RELEASE(s_ColorMatrixProgram); + SAFE_RELEASE(s_ColorCopyProgram); + SAFE_RELEASE(s_DepthMatrixProgram); + SAFE_RELEASE(s_ClearProgram); + + Clear(); + g_ps_disk_cache.Sync(); + g_ps_disk_cache.Close(); +} + +bool PixelShaderCache::SetShader(bool dstAlpha) +{ + PIXELSHADERUID uid; + GetPixelShaderId(&uid, dstAlpha); + + // check if the shader is already set + if (uid == last_pixel_shader_uid && PixelShaders[uid].frameCount == frameCount) + { + PSCache::const_iterator iter = PixelShaders.find(uid); + return (iter != PixelShaders.end() && iter->second.shader); + } + + memcpy(&last_pixel_shader_uid, &uid, sizeof(PIXELSHADERUID)); + + // check if the shader is already in the cache + PSCache::iterator iter = PixelShaders.find(uid); + if (iter != PixelShaders.end()) + { + iter->second.frameCount = frameCount; + const PSCacheEntry &entry = iter->second; + last_entry = &entry; + + D3D::gfxstate->SetPShader(entry.shader); + return (entry.shader != NULL); + } + + // need to compile a new shader + const char* code = GeneratePixelShaderCode(dstAlpha, API_D3D11); + + D3DBlob* pbytecode; + if (!D3D::CompilePixelShader(code, (unsigned int)strlen(code), &pbytecode)) + { + PanicAlert("Failed to compile Pixel Shader:\n\n%s", code); + return false; + } + + // insert the bytecode into the caches + g_ps_disk_cache.Append((u8*)&uid, sizeof(uid), (const u8*)pbytecode->Data(), pbytecode->Size()); + g_ps_disk_cache.Sync(); + + bool result = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size()); + D3D::gfxstate->SetPShader(last_entry->shader); + pbytecode->Release(); + return result; +} + +bool PixelShaderCache::InsertByteCode(const PIXELSHADERUID &uid, void* bytecode, unsigned int bytecodelen) +{ + ID3D11PixelShader* shader = D3D::CreatePixelShaderFromByteCode(bytecode, bytecodelen); + if (shader == NULL) + { + PanicAlert("Failed to create pixel shader at %s %d\n", __FILE__, __LINE__); + return false; + } + // TODO: Somehow make the debug name a bit more specific + D3D::SetDebugObjectName((ID3D11DeviceChild*)shader, "a pixel shader of PixelShaderCache"); + + // make an entry in the table + PSCacheEntry newentry; + newentry.shader = shader; + newentry.frameCount = frameCount; + PixelShaders[uid] = newentry; + last_entry = &PixelShaders[uid]; + + INCSTAT(stats.numPixelShadersCreated); + SETSTAT(stats.numPixelShadersAlive, PixelShaders.size()); + + return true; +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_PixelShaderCache.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_PixelShaderCache.h new file mode 100644 index 0000000000..05f7ea12ca --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_PixelShaderCache.h @@ -0,0 +1,74 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include + +// Common +#include "Common.h" +#include "LinearDiskCache.h" + +// VideoCommon +#include "PixelShaderGen.h" +#include "VertexShaderGen.h" + +// DX11 +#include "DX11_D3DBase.h" + +#include "../PixelShaderCache.h" + +namespace DX11 +{ + +class PixelShaderCache : public ::PixelShaderCacheBase +{ +public: + PixelShaderCache(); + ~PixelShaderCache(); + + void Clear(); + + bool SetShader(bool dstAlpha); + static bool InsertByteCode(const PIXELSHADERUID &uid, void* bytecode, unsigned int bytecodelen); + + static ID3D11PixelShader* GetColorMatrixProgram(); + static ID3D11PixelShader* GetColorCopyProgram(); + static ID3D11PixelShader* GetDepthMatrixProgram(); + static ID3D11PixelShader* GetClearProgram(); + + void SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4); + void SetPSConstant4fv(unsigned int const_number, const float *f); + void SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float *f); + +private: + struct PSCacheEntry + { + ID3D11PixelShader* shader; + int frameCount; + + PSCacheEntry() : shader(NULL), frameCount(0) {} + void Destroy() { SAFE_RELEASE(shader); } + }; + + typedef std::map PSCache; + + static PSCache PixelShaders; + static const PSCacheEntry* last_entry; +}; + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_Render.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_Render.cpp new file mode 100644 index 0000000000..4a3bb58108 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_Render.cpp @@ -0,0 +1,855 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +// Common +#include "Common.h" +#include "StringUtil.h" +#include "Atomic.h" +#include "FileUtil.h" +#include "Thread.h" +#include "Timer.h" +#include "Statistics.h" +#include "OnScreenDisplay.h" + +// VideoCommon +#include "VideoConfig.h" +#include "OpcodeDecoding.h" +#include "BPStructs.h" +#include "XFStructs.h" +#include "PixelShaderManager.h" +#include "VertexShaderManager.h" +#include "VertexLoaderManager.h" +#include "AVIDump.h" +#include "Fifo.h" +#include "DLCache.h" + +// DX11 +#include "DX11_D3DUtil.h" +#include "DX11_VertexManager.h" +#include "DX11_VertexShaderCache.h" +#include "DX11_PixelShaderCache.h" +#include "DX11_TextureCache.h" +#include "DX11_FramebufferManager.h" +#include "DX11_Render.h" + +#include "../EmuWindow.h" +#include "../Main.h" + +namespace DX11 +{ + +ID3D11Buffer* access_efb_cbuf = NULL; +ID3D11DepthStencilState* cleardepthstates[2] = {NULL}; +ID3D11RasterizerState* clearraststate = NULL; +ID3D11BlendState* resetblendstate = NULL; +ID3D11DepthStencilState* resetdepthstate = NULL; +ID3D11RasterizerState* resetraststate = NULL; + +bool reset_called = false; + +// state translation lookup tables +static const D3D11_BLEND d3dSrcFactors[8] = +{ + D3D11_BLEND_ZERO, + D3D11_BLEND_ONE, + D3D11_BLEND_DEST_COLOR, + D3D11_BLEND_INV_DEST_COLOR, + D3D11_BLEND_SRC_ALPHA, + D3D11_BLEND_INV_SRC_ALPHA, + D3D11_BLEND_DEST_ALPHA, + D3D11_BLEND_INV_DEST_ALPHA +}; + +static const D3D11_BLEND d3dDestFactors[8] = +{ + D3D11_BLEND_ZERO, + D3D11_BLEND_ONE, + D3D11_BLEND_SRC_COLOR, + D3D11_BLEND_INV_SRC_COLOR, + D3D11_BLEND_SRC_ALPHA, + D3D11_BLEND_INV_SRC_ALPHA, + D3D11_BLEND_DEST_ALPHA, + D3D11_BLEND_INV_DEST_ALPHA +}; + +// 0 0x00 +// 1 Source & destination +// 2 Source & ~destination +// 3 Source +// 4 ~Source & destination +// 5 Destination +// 6 Source ^ destination = Source & ~destination | ~Source & destination +// 7 Source | destination + +// 8 ~(Source | destination) +// 9 ~(Source ^ destination) = ~Source & ~destination | Source & destination +// 10 ~Destination +// 11 Source | ~destination +// 12 ~Source +// 13 ~Source | destination +// 14 ~(Source & destination) +// 15 0xff + +static const D3D11_BLEND_OP d3dLogicOps[16] = +{ + D3D11_BLEND_OP_ADD,//0 + D3D11_BLEND_OP_ADD,//1 + D3D11_BLEND_OP_SUBTRACT,//2 + D3D11_BLEND_OP_ADD,//3 + D3D11_BLEND_OP_REV_SUBTRACT,//4 + D3D11_BLEND_OP_ADD,//5 + D3D11_BLEND_OP_MAX,//6 + D3D11_BLEND_OP_ADD,//7 + + D3D11_BLEND_OP_MAX,//8 + D3D11_BLEND_OP_MAX,//9 + D3D11_BLEND_OP_ADD,//10 + D3D11_BLEND_OP_ADD,//11 + D3D11_BLEND_OP_ADD,//12 + D3D11_BLEND_OP_ADD,//13 + D3D11_BLEND_OP_ADD,//14 + D3D11_BLEND_OP_ADD//15 +}; + +static const D3D11_BLEND d3dLogicOpSrcFactors[16] = +{ + D3D11_BLEND_ZERO,//0 + D3D11_BLEND_DEST_COLOR,//1 + D3D11_BLEND_ONE,//2 + D3D11_BLEND_ONE,//3 + D3D11_BLEND_DEST_COLOR,//4 + D3D11_BLEND_ZERO,//5 + D3D11_BLEND_INV_DEST_COLOR,//6 + D3D11_BLEND_INV_DEST_COLOR,//7 + + D3D11_BLEND_INV_SRC_COLOR,//8 + D3D11_BLEND_INV_SRC_COLOR,//9 + D3D11_BLEND_INV_DEST_COLOR,//10 + D3D11_BLEND_ONE,//11 + D3D11_BLEND_INV_SRC_COLOR,//12 + D3D11_BLEND_INV_SRC_COLOR,//13 + D3D11_BLEND_INV_DEST_COLOR,//14 + D3D11_BLEND_ONE//15 +}; + +static const D3D11_BLEND d3dLogicOpDestFactors[16] = +{ + D3D11_BLEND_ZERO,//0 + D3D11_BLEND_ZERO,//1 + D3D11_BLEND_INV_SRC_COLOR,//2 + D3D11_BLEND_ZERO,//3 + D3D11_BLEND_ONE,//4 + D3D11_BLEND_ONE,//5 + D3D11_BLEND_INV_SRC_COLOR,//6 + D3D11_BLEND_ONE,//7 + + D3D11_BLEND_INV_DEST_COLOR,//8 + D3D11_BLEND_SRC_COLOR,//9 + D3D11_BLEND_INV_DEST_COLOR,//10 + D3D11_BLEND_INV_DEST_COLOR,//11 + D3D11_BLEND_INV_SRC_COLOR,//12 + D3D11_BLEND_ONE,//13 + D3D11_BLEND_INV_SRC_COLOR,//14 + D3D11_BLEND_ONE//15 +}; + +static const D3D11_CULL_MODE d3dCullModes[4] = +{ + D3D11_CULL_NONE, + D3D11_CULL_BACK, + D3D11_CULL_FRONT, + D3D11_CULL_BACK +}; + +static const D3D11_COMPARISON_FUNC d3dCmpFuncs[8] = +{ + D3D11_COMPARISON_NEVER, + D3D11_COMPARISON_LESS, + D3D11_COMPARISON_EQUAL, + D3D11_COMPARISON_LESS_EQUAL, + D3D11_COMPARISON_GREATER, + D3D11_COMPARISON_NOT_EQUAL, + D3D11_COMPARISON_GREATER_EQUAL, + D3D11_COMPARISON_ALWAYS +}; + +#define TEXF_NONE 0 +#define TEXF_POINT 1 +#define TEXF_LINEAR 2 +static const unsigned int d3dMipFilters[4] = +{ + TEXF_NONE, + TEXF_POINT, + TEXF_LINEAR, + TEXF_NONE, //reserved +}; + +static const D3D11_TEXTURE_ADDRESS_MODE d3dClamps[4] = +{ + D3D11_TEXTURE_ADDRESS_CLAMP, + D3D11_TEXTURE_ADDRESS_WRAP, + D3D11_TEXTURE_ADDRESS_MIRROR, + D3D11_TEXTURE_ADDRESS_WRAP //reserved +}; + + +// what are these 2? +//bool Renderer::Allow2x() +//{ +// return false; +//} +// +//bool Renderer::AllowCustom() +//{ +// return false; +//} + +void Renderer::SetupDeviceObjects() +{ + g_framebuffer_manager = new FramebufferManager; + + HRESULT hr; + float colmat[20]= {0.0f}; + colmat[0] = colmat[5] = colmat[10] = 1.0f; + D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(20*sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT); + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = colmat; + hr = D3D::device->CreateBuffer(&cbdesc, &data, &access_efb_cbuf); + CHECK(hr==S_OK, "Create constant buffer for AccessEFB"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)access_efb_cbuf, "constant buffer for AccessEFB"); + + D3D11_DEPTH_STENCIL_DESC ddesc; + ddesc.DepthEnable = FALSE; + ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + ddesc.DepthFunc = D3D11_COMPARISON_ALWAYS; + ddesc.StencilEnable = FALSE; + ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + hr = D3D::device->CreateDepthStencilState(&ddesc, &cleardepthstates[0]); + CHECK(hr==S_OK, "Create depth state for ClearScreen"); + ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + ddesc.DepthEnable = TRUE; + hr = D3D::device->CreateDepthStencilState(&ddesc, &cleardepthstates[1]); + CHECK(hr==S_OK, "Create depth state for ClearScreen"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)cleardepthstates[0], "depth state for ClearScreen (depth buffer disabled)"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)cleardepthstates[1], "depth state for ClearScreen (depth buffer enabled)"); + + // TODO: once multisampling gets implemented, this might need to be changed + D3D11_RASTERIZER_DESC rdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0.f, false, true, false, false); + hr = D3D::device->CreateRasterizerState(&rdesc, &clearraststate); + CHECK(hr==S_OK, "Create rasterizer state for ClearScreen"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)clearraststate, "rasterizer state for ClearScreen"); + + D3D11_BLEND_DESC blenddesc; + blenddesc.AlphaToCoverageEnable = FALSE; + blenddesc.IndependentBlendEnable = FALSE; + blenddesc.RenderTarget[0].BlendEnable = FALSE; + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + hr = D3D::device->CreateBlendState(&blenddesc, &resetblendstate); + CHECK(hr==S_OK, "Create blend state for ResetAPIState"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)resetblendstate, "blend state for ResetAPIState"); + + ddesc.DepthEnable = FALSE; + ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + ddesc.DepthFunc = D3D11_COMPARISON_LESS; + ddesc.StencilEnable = FALSE; + ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + hr = D3D::device->CreateDepthStencilState(&ddesc, &resetdepthstate); + CHECK(hr==S_OK, "Create depth state for ResetAPIState"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)resetdepthstate, "depth stencil state for ResetAPIState"); + + // this might need to be changed once multisampling support gets added + D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0.f, false, false, false, false); + hr = D3D::device->CreateRasterizerState(&rastdesc, &resetraststate); + CHECK(hr==S_OK, "Create rasterizer state for ClearScreen"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)resetraststate, "rasterizer state for ResetAPIState"); +} + +void Renderer::TeardownDeviceObjects() +{ + delete g_framebuffer_manager; + + SAFE_RELEASE(access_efb_cbuf); + SAFE_RELEASE(cleardepthstates[0]); + SAFE_RELEASE(cleardepthstates[1]); + SAFE_RELEASE(clearraststate); + SAFE_RELEASE(resetblendstate); + SAFE_RELEASE(resetdepthstate); + SAFE_RELEASE(resetraststate); +} + +Renderer::Renderer() +{ + UpdateActiveConfig(); + + //int x, y, w_temp, h_temp; + //g_VideoInitialize.pRequestWindowSize(x, y, w_temp, h_temp); + + D3D::Create(EmuWindow::GetWnd()); + + FramebufferSize(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight()); + + SetupDeviceObjects(); + + for (unsigned int stage = 0; stage < 8; stage++) + D3D::gfxstate->samplerdesc[stage].MaxAnisotropy = g_ActiveConfig.iMaxAnisotropy; + + float ClearColor[4] = { 0.f, 0.f, 0.f, 0.f }; + D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), ClearColor); + D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(), D3D11_CLEAR_DEPTH, 1.f, 0); + + D3D11_VIEWPORT vp = CD3D11_VIEWPORT((float)(s_Fulltarget_width - s_target_width) / 2.f, + (float)(s_Fulltarget_height - s_target_height) / 2.f, + (float)s_target_width, (float)s_target_height); + D3D::context->RSSetViewports(1, &vp); + D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV()); + D3D::BeginFrame(); + D3D::gfxstate->rastdesc.ScissorEnable = TRUE; + + // temporary, maybe + D3D::InitUtils(); + + reset_called = false; + //return true; +} + +Renderer::~Renderer() +{ + // temporary, maybe + D3D::ShutdownUtils(); + + TeardownDeviceObjects(); + D3D::EndFrame(); + D3D::Present(); + D3D::Close(); +} + +bool Renderer::CheckForResize() +{ + while (EmuWindow::IsSizing()) + Sleep(10); + + if (EmuWindow::GetParentWnd()) + { + // re-stretch window to parent window size again, if it has a parent window. + RECT rcParentWindow; + GetWindowRect(EmuWindow::GetParentWnd(), &rcParentWindow); + int width = rcParentWindow.right - rcParentWindow.left; + int height = rcParentWindow.bottom - rcParentWindow.top; + if (width != s_backbuffer_width || height != s_backbuffer_height) + ::MoveWindow(EmuWindow::GetWnd(), 0, 0, width, height, FALSE); + } + RECT rcWindow; + GetClientRect(EmuWindow::GetWnd(), &rcWindow); + int client_width = rcWindow.right - rcWindow.left; + int client_height = rcWindow.bottom - rcWindow.top; + + // sanity check + return ((client_width != s_backbuffer_width || client_height != s_backbuffer_height) && + client_width >= 4 && client_height >= 4); +} + +bool Renderer::SetScissorRect() +{ + EFBRectangle rc; + if (g_renderer->SetScissorRect(rc)) + { + D3D::context->RSSetScissorRects(1, (D3D11_RECT*)&rc); + return true; + } + else + { + //WARN_LOG(VIDEO, "Bad scissor rectangle: %i %i %i %i", rc.left, rc.top, rc.right, rc.bottom); + const int Xstride = (s_Fulltarget_width - s_target_width) / 2; + const int Ystride = (s_Fulltarget_height - s_target_height) / 2; + rc.left = Xstride; + rc.top = Ystride; + rc.right = Xstride + s_target_width; + rc.bottom = Ystride + s_target_height; + + D3D::context->RSSetScissorRects(1, (D3D11_RECT*)&rc); + return false; + } +} + +void Renderer::SetColorMask() +{ + UINT8 color_mask = 0; + if (bpmem.blendmode.alphaupdate) + color_mask |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + + if (bpmem.blendmode.colorupdate) + color_mask |= D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE; + + D3D::gfxstate->SetRenderTargetWriteMask(color_mask); +} + +u32 Renderer::AccessEFB(EFBAccessType type, int x, int y) +{ + ID3D11Texture2D* read_tex; + + if (!g_ActiveConfig.bEFBAccessEnable) + return 0; + + if (type == POKE_Z || type == POKE_COLOR) + { + static bool alert_only_once = true; + if (!alert_only_once) + return 0; + PanicAlert("Poke EFB not implemented"); + alert_only_once = false; + return 0; + } + + // get the rectangular target region covered by the EFB pixel + EFBRectangle efbPixelRc; + efbPixelRc.left = x; + efbPixelRc.top = y; + efbPixelRc.right = x + 1; + efbPixelRc.bottom = y + 1; + + TargetRectangle targetPixelRc = ConvertEFBRectangle(efbPixelRc); + + u32 z = 0; + float val = 0.0f; + D3D11_RECT RectToLock = CD3D11_RECT(targetPixelRc.left, + targetPixelRc.top, targetPixelRc.right, targetPixelRc.bottom); + if (type == PEEK_Z) + { + // depth buffers can only be completely CopySubresourceRegion'ed, so we're using drawShadedTexQuad instead + + RectToLock.bottom+=2; + RectToLock.right+=1; + RectToLock.top-=1; + RectToLock.left-=2; + if ((RectToLock.bottom - RectToLock.top) > 4) + RectToLock.bottom--; + if ((RectToLock.right - RectToLock.left) > 4) + RectToLock.left++; + + ResetAPIState(); // reset any game specific settings + + // Stretch picture with increased internal resolution + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, 4.f, 4.f); + D3D::context->RSSetViewports(1, &vp); + D3D::context->PSSetConstantBuffers(0, 1, &access_efb_cbuf); + D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBDepthReadTexture()->GetRTV(), NULL); + D3D::SetPointCopySampler(); + D3D::drawShadedTexQuad(FramebufferManager::GetEFBDepthTexture()->GetSRV(), + &RectToLock, + GetFullTargetWidth(), + GetFullTargetHeight(), + PixelShaderCache::GetDepthMatrixProgram(), + VertexShaderCache::GetSimpleVertexShader(), + VertexShaderCache::GetSimpleInputLayout()); + + D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV()); + RestoreAPIState(); + RectToLock = CD3D11_RECT(0, 0, 4, 4); + + // copy to system memory + D3D11_BOX box = CD3D11_BOX(0, 0, 0, 4, 4, 1); + read_tex = FramebufferManager::GetEFBDepthStagingBuffer(); + D3D::context->CopySubresourceRegion(read_tex, 0, 0, 0, 0, FramebufferManager::GetEFBDepthReadTexture()->GetTex(), 0, &box); + } + else + { + // we can directly copy to system memory here + read_tex = FramebufferManager::GetEFBColorStagingBuffer(); + D3D11_BOX box = CD3D11_BOX(RectToLock.left, RectToLock.top, 0, RectToLock.right, RectToLock.bottom, 1); + D3D::context->CopySubresourceRegion(read_tex, 0, 0, 0, 0, FramebufferManager::GetEFBColorTexture()->GetTex(), 0, &box); + RectToLock = CD3D11_RECT(0, 0, 1, 1); + } + + // read the data from system memory + D3D11_MAPPED_SUBRESOURCE map; + D3D::context->Map(read_tex, 0, D3D11_MAP_READ, 0, &map); + + switch(type) + { + case PEEK_Z: + val = *(float*)map.pData; + z = (u32)(val * 0xffffff); + break; + + case PEEK_COLOR: + z = *(u32*)map.pData; + break; + + // TODO: Implement POKE_Z and POKE_COLOR + default: + break; + } + D3D::context->Unmap(read_tex, 0); + return z; +} + +// Called from VertexShaderManager +void Renderer::UpdateViewport() +{ + // reversed gxsetviewport(xorig, yorig, width, height, nearz, farz) + // [0] = width/2 + // [1] = height/2 + // [2] = 16777215 * (farz - nearz) + // [3] = xorig + width/2 + 342 + // [4] = yorig + height/2 + 342 + // [5] = 16777215 * farz + const int old_fulltarget_w = s_Fulltarget_width; + const int old_fulltarget_h = s_Fulltarget_height; + + int scissorXOff = bpmem.scissorOffset.x * 2; + int scissorYOff = bpmem.scissorOffset.y * 2; + + float MValueX = GetTargetScaleX(); + float MValueY = GetTargetScaleY(); + + int Xstride = (s_Fulltarget_width - s_target_width) / 2; + int Ystride = (s_Fulltarget_height - s_target_height) / 2; + + // Stretch picture with increased internal resolution + int X = (int)(ceil(xfregs.rawViewport[3] - xfregs.rawViewport[0] - (scissorXOff)) * MValueX) + Xstride; + int Y = (int)(ceil(xfregs.rawViewport[4] + xfregs.rawViewport[1] - (scissorYOff)) * MValueY) + Ystride; + int Width = (int)ceil((int)(2 * xfregs.rawViewport[0]) * MValueX); + int Height = (int)ceil((int)(-2 * xfregs.rawViewport[1]) * MValueY); + if (Width < 0) + { + X += Width; + Width*=-1; + } + if (Height < 0) + { + Y += Height; + Height *= -1; + } + bool sizeChanged = false; + if (X < 0) + { + s_Fulltarget_width -= 2 * X; + X = 0; + sizeChanged=true; + } + if (Y < 0) + { + s_Fulltarget_height -= 2 * Y; + Y = 0; + sizeChanged=true; + } + + float newx = (float)X; + float newy = (float)Y; + float newwidth = (float)Width; + float newheight = (float)Height; + if (sizeChanged) + { + // Make sure that the requested size is actually supported by the GFX driver + if (s_Fulltarget_width > (int)D3D::GetMaxTextureSize() || s_Fulltarget_height > (int)D3D::GetMaxTextureSize()) + { + // Skip EFB recreation and viewport setting. Most likely causes glitches in this case, but prevents crashes at least + ERROR_LOG(VIDEO, "Tried to set a viewport which is too wide to emulate with Direct3D11. Requested EFB size is %dx%d\n", s_Fulltarget_width, s_Fulltarget_height); + + // Fix the viewport to fit to the old EFB size, TODO: Check this for off-by-one errors + newx *= (float)old_fulltarget_w / (float)s_Fulltarget_width; + newy *= (float)old_fulltarget_h / (float)s_Fulltarget_height; + newwidth *= (float)old_fulltarget_w / (float)s_Fulltarget_width; + newheight *= (float)old_fulltarget_h / (float)s_Fulltarget_height; + + s_Fulltarget_width = old_fulltarget_w; + s_Fulltarget_height = old_fulltarget_h; + } + else + { + D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL); + + delete g_framebuffer_manager; + g_framebuffer_manager = new FramebufferManager; + + D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV()); + } + } + + // some games set invalids values MinDepth and MaxDepth so fix them to the max an min allowed and let the shaders do this work + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(newx, newy, newwidth, newheight, + 0.f, // (xfregs.rawViewport[5] - xfregs.rawViewport[2]) / 16777216.0f; + 1.f); // xfregs.rawViewport[5] / 16777216.0f; + D3D::context->RSSetViewports(1, &vp); +} + +// Tino: color is passed in bgra mode so need to convert it to rgba +void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) +{ + const TargetRectangle targetRc = ConvertEFBRectangle(rc); + // update the view port for clearing the picture + D3D11_VIEWPORT vp = CD3D11_VIEWPORT((float)targetRc.left, (float)targetRc.top, (float)targetRc.GetWidth(), (float)targetRc.GetHeight(), + 0.f, + 1.f); + D3D::context->RSSetViewports(1, &vp); + + // always set the scissor in case it was set by the game and has not been reset + D3D11_RECT sirc = CD3D11_RECT(targetRc.left, targetRc.top, targetRc.right, targetRc.bottom); + D3D::context->RSSetScissorRects(1, &sirc); + u32 rgbaColor = (color & 0xFF00FF00) | ((color >> 16) & 0xFF) | ((color << 16) & 0xFF0000); + D3D::stateman->PushDepthState(cleardepthstates[zEnable]); + D3D::stateman->PushRasterizerState(clearraststate); + //D3D::stateman->PushBlendState(resetblendstate); temporarily commented until I find the cause of the blending issue in mkwii (see next line) + D3D::gfxstate->ApplyState(); // TODO (neobrain): find out whether this breaks/fixes anything or can just be dropped. Might obsolete the comment above this line + D3D::drawClearQuad(rgbaColor, (z & 0xFFFFFF) / float(0xFFFFFF), PixelShaderCache::GetClearProgram(), VertexShaderCache::GetClearVertexShader(), VertexShaderCache::GetClearInputLayout()); + D3D::gfxstate->Reset(); + D3D::stateman->PopDepthState(); + D3D::stateman->PopRasterizerState(); +// D3D::stateman->PopBlendState(); + UpdateViewport(); + SetScissorRect(); +} + +void Renderer::SetBlendMode(bool forceUpdate) +{ + if (bpmem.blendmode.logicopenable) + return; + + if (bpmem.blendmode.subtract) // enable blending src 1 dst 1 + { + D3D::gfxstate->SetAlphaBlendEnable(true); + D3D::gfxstate->SetBlendOp(D3D11_BLEND_OP_REV_SUBTRACT); + D3D::gfxstate->SetSrcBlend(d3dSrcFactors[1]); + D3D::gfxstate->SetDestBlend(d3dDestFactors[1]); + } + else + { + D3D::gfxstate->SetAlphaBlendEnable(bpmem.blendmode.blendenable && (!( bpmem.blendmode.srcfactor == 1 && bpmem.blendmode.dstfactor == 0))); + if (bpmem.blendmode.blendenable && (!( bpmem.blendmode.srcfactor == 1 && bpmem.blendmode.dstfactor == 0))) + { + D3D::gfxstate->SetBlendOp(D3D11_BLEND_OP_ADD); + D3D::gfxstate->SetSrcBlend(d3dSrcFactors[bpmem.blendmode.srcfactor]); + D3D::gfxstate->SetDestBlend(d3dDestFactors[bpmem.blendmode.dstfactor]); + } + + } +} + +void Renderer::PrepareXFBCopy(const TargetRectangle &dst_rect) +{ + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)s_backbuffer_width, (float)s_backbuffer_height); + D3D::context->RSSetViewports(1, &vp); + static const float clear_color[4] = { 0.f, 0.f, 0.f, 1.f }; + + D3D::context->ClearRenderTargetView(D3D::GetBackBuffer()->GetRTV(), clear_color); + + int x = dst_rect.left; + int y = dst_rect.top; + int width = dst_rect.right - dst_rect.left; + int height = dst_rect.bottom - dst_rect.top; + + if (x < 0) x = 0; + if (y < 0) y = 0; + if (x > s_backbuffer_width) x = s_backbuffer_width; + if (y > s_backbuffer_height) y = s_backbuffer_height; + if (width < 0) width = 0; + if (height < 0) height = 0; + if (width > (s_backbuffer_width - x)) width = s_backbuffer_width - x; + if (height > (s_backbuffer_height - y)) height = s_backbuffer_height - y; + + vp = CD3D11_VIEWPORT((float)x, (float)y, (float)width, (float)height); + D3D::context->RSSetViewports(1, &vp); + + D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL); + + // activate linear filtering for the buffer copies + D3D::SetLinearCopySampler(); +} + +void Renderer::Draw(const XFBSourceBase* xfbSource, const TargetRectangle& sourceRc, + const MathUtil::Rectangle& drawRc, const EFBRectangle& rc) +{ + if (xfbSource) + { + D3DTexture2D* const tex = ((XFBSource*)xfbSource)->tex; + + // TODO: + PanicAlert("DX11 XFB"); + } + else + { + D3DTexture2D* const tex = FramebufferManager::GetEFBColorTexture(); + + D3D::drawShadedTexQuad(tex->GetSRV(), sourceRc.AsRECT(), GetFullTargetWidth(), + GetFullTargetHeight(), PixelShaderCache::GetColorCopyProgram(), + VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout()); + } +} + +void Renderer::EndFrame() +{ + D3D::EndFrame(); +} + +void Renderer::Present() +{ + D3D::Present(); + + // TODO: Aren't we still holding a reference to the back buffer right now? + // TODO: this was right before getting the backbuffer size in DX11::Renderer::Swap + D3D::Reset(); +} + +void Renderer::GetBackBufferSize(int* w, int* h) +{ + *w = D3D::GetBackBufferWidth(); + *h = D3D::GetBackBufferHeight(); +} + +void Renderer::RecreateFramebufferManger() +{ + // this good? + D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL); + delete g_framebuffer_manager; + g_framebuffer_manager = new FramebufferManager; +} + +void Renderer::BeginFrame() +{ + D3D::BeginFrame(); + D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV()); +} + +// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing +void Renderer::ResetAPIState() +{ + D3D::gfxstate->Reset(); + D3D::stateman->PushBlendState(resetblendstate); + D3D::stateman->PushDepthState(resetdepthstate); + D3D::stateman->PushRasterizerState(resetraststate); + D3D::stateman->Apply(); + reset_called = true; +} + +void Renderer::RestoreAPIState() +{ + // gets us back into a more game-like state. + if (reset_called) + { + D3D::stateman->PopBlendState(); + D3D::stateman->PopDepthState(); + D3D::stateman->PopRasterizerState(); + } + UpdateViewport(); + SetScissorRect(); + D3D::gfxstate->ApplyState(); + reset_called = false; +} + +void Renderer::SetGenerationMode() +{ + // rastdesc.FrontCounterClockwise must be false for this to work + D3D::gfxstate->rastdesc.CullMode = d3dCullModes[bpmem.genMode.cullmode]; +} + +void Renderer::SetDepthMode() +{ + if (bpmem.zmode.testenable) + { + D3D::gfxstate->depthdesc.DepthEnable = TRUE; + D3D::gfxstate->depthdesc.DepthWriteMask = bpmem.zmode.updateenable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + D3D::gfxstate->depthdesc.DepthFunc = d3dCmpFuncs[bpmem.zmode.func]; + } + else + { + D3D::gfxstate->depthdesc.DepthEnable = FALSE; + D3D::gfxstate->depthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + } +} + +void Renderer::SetLogicOpMode() +{ + if (bpmem.blendmode.logicopenable && bpmem.blendmode.logicmode != 3) + { + s_blendMode = 0; + D3D::gfxstate->SetAlphaBlendEnable(true); + D3D::gfxstate->SetBlendOp(d3dLogicOps[bpmem.blendmode.logicmode]); + D3D::gfxstate->SetSrcBlend(d3dLogicOpSrcFactors[bpmem.blendmode.logicmode]); + D3D::gfxstate->SetDestBlend(d3dLogicOpDestFactors[bpmem.blendmode.logicmode]); + } + else + { + SetBlendMode(true); + } +} + +void Renderer::SetSamplerState(int stage, int texindex) +{ + const FourTexUnits &tex = bpmem.tex[texindex]; + const TexMode0 &tm0 = tex.texMode0[stage]; + const TexMode1 &tm1 = tex.texMode1[stage]; + + unsigned int mip; + mip = (tm0.min_filter == 8) ? TEXF_NONE : d3dMipFilters[tm0.min_filter & 3]; + if ((tm0.min_filter & 3) && (tm0.min_filter != 8) && ((tm1.max_lod >> 4) == 0)) mip = TEXF_NONE; + + if (texindex) + stage += 4; + + // TODO: Clarify whether these values are correct + // NOTE: since there's no "no filter" in DX11 we're using point filters in these cases + if (tm0.min_filter & 4) // linear min filter + { + if (tm0.mag_filter) // linear mag filter + { + if (mip == TEXF_NONE) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT); + else if (mip == TEXF_POINT) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT); + else if (mip == TEXF_LINEAR) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_MIP_LINEAR); + } + else // point mag filter + { + if (mip == TEXF_NONE) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT); + else if (mip == TEXF_POINT) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT); + else if (mip == TEXF_LINEAR) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR); + } + } + else // point min filter + { + if (tm0.mag_filter) // linear mag filter + { + if (mip == TEXF_NONE) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT); + else if (mip == TEXF_POINT) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT); + else if (mip == TEXF_LINEAR) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR); + } + else // point mag filter + { + if (mip == TEXF_NONE) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_MIP_POINT); + else if (mip == TEXF_POINT) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_MIP_POINT); + else if (mip == TEXF_LINEAR) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR); + } + } + + D3D::gfxstate->samplerdesc[stage].AddressU = d3dClamps[tm0.wrap_s]; + D3D::gfxstate->samplerdesc[stage].AddressV = d3dClamps[tm0.wrap_t]; + + D3D::gfxstate->samplerdesc[stage].MipLODBias = (float)tm0.lod_bias / 32.0f; + D3D::gfxstate->samplerdesc[stage].MaxLOD = (float)tm1.max_lod / 16.f; + D3D::gfxstate->samplerdesc[stage].MinLOD = (float)tm1.min_lod / 16.f; +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_Render.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_Render.h new file mode 100644 index 0000000000..a0af4f4a20 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_Render.h @@ -0,0 +1,52 @@ + +#pragma once + +#include "MathUtil.h" + +#include "VideoCommon.h" +#include "Renderer.h" +#include "pluginspecs_video.h" + +namespace DX11 +{ + +class Renderer : public ::RendererBase +{ +public: + Renderer(); + ~Renderer(); + + // What's the real difference between these? Too similar names. + void ResetAPIState(); + void RestoreAPIState(); + + static void SetupDeviceObjects(); + static void TeardownDeviceObjects(); + + void SetColorMask(); + void SetBlendMode(bool forceUpdate); + bool SetScissorRect(); + + void SetGenerationMode(); + void SetDepthMode(); + void SetLogicOpMode(); + void SetSamplerState(int stage,int texindex); + + u32 AccessEFB(EFBAccessType type, int x, int y); + + void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z); + void UpdateViewport(); + + // virtual funcs used by RendererBase::Swap + void PrepareXFBCopy(const TargetRectangle &dst_rect); + void Draw(const XFBSourceBase* xfbSource, const TargetRectangle& sourceRc, + const MathUtil::Rectangle& drawRc, const EFBRectangle& rc); + void EndFrame(); + void Present(); + bool CheckForResize(); + void GetBackBufferSize(int* w, int* h); + void RecreateFramebufferManger(); + void BeginFrame(); +}; + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_TextureCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_TextureCache.cpp new file mode 100644 index 0000000000..38b4a4c55f --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_TextureCache.cpp @@ -0,0 +1,230 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +// Common +#include "CommonPaths.h" +#include "FileUtil.h" +#include "MemoryUtil.h" +#include "Hash.h" + +// VideoCommon +#include "VideoConfig.h" +#include "Statistics.h" +#include "PixelShaderManager.h" +#include "VertexShaderManager.h" +#include "TextureDecoder.h" +#include "HiresTextures.h" + +// DX11 +#include "DX11_D3DBase.h" +#include "DX11_D3DTexture.h" +#include "DX11_D3DUtil.h" +#include "DX11_FramebufferManager.h" +#include "DX11_PixelShaderCache.h" +#include "DX11_VertexShaderCache.h" +#include "DX11_TextureCache.h" + +#include "../Main.h" + +namespace DX11 +{ + +ID3D11BlendState* efbcopyblendstate = NULL; +ID3D11RasterizerState* efbcopyraststate = NULL; +ID3D11DepthStencilState* efbcopydepthstate = NULL; +ID3D11Buffer* efbcopycbuf[20] = {}; + +TextureCache::TCacheEntry::~TCacheEntry() +{ + SAFE_RELEASE(texture); +} + +void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level) +{ + D3D::ReplaceRGBATexture2D(texture->GetTex(), TextureCache::temp, width, height, expanded_width, level, usage); +} + +void TextureCache::TCacheEntry::FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect) +{ + // stretch picture with increased internal resolution + const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)Scaledw, (float)Scaledh); + D3D::context->RSSetViewports(1, &vp); + + // set transformation + if (NULL == efbcopycbuf[cbufid]) + { + const D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(20 * sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT); + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = colmat; + HRESULT hr = D3D::device->CreateBuffer(&cbdesc, &data, &efbcopycbuf[cbufid]); + CHECK(SUCCEEDED(hr), "Create efb copy constant buffer %d", cbufid); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopycbuf[cbufid], "a constant buffer used in TextureCache::CopyRenderTargetToTexture"); + } + D3D::context->PSSetConstantBuffers(0, 1, &efbcopycbuf[cbufid]); + + const TargetRectangle targetSource = g_renderer->ConvertEFBRectangle(source_rect); + // TODO: try targetSource.asRECT(); + const D3D11_RECT sourcerect = CD3D11_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom); + + // Use linear filtering if (bScaleByHalf), use point filtering otherwise + if (bScaleByHalf) + D3D::SetLinearCopySampler(); + else + D3D::SetPointCopySampler(); + + D3D::stateman->PushBlendState(efbcopyblendstate); + D3D::stateman->PushRasterizerState(efbcopyraststate); + D3D::stateman->PushDepthState(efbcopydepthstate); + + D3D::context->OMSetRenderTargets(1, &texture->GetRTV(), NULL); + + D3D::drawShadedTexQuad( + (bFromZBuffer) ? FramebufferManager::GetEFBDepthTexture()->GetSRV() : FramebufferManager::GetEFBColorTexture()->GetSRV(), + &sourcerect, g_renderer->GetFullTargetWidth(), g_renderer->GetFullTargetHeight(), + (bFromZBuffer) ? PixelShaderCache::GetDepthMatrixProgram() : PixelShaderCache::GetColorMatrixProgram(), + VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout()); + + D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV()); + + D3D::stateman->PopBlendState(); + D3D::stateman->PopDepthState(); + D3D::stateman->PopRasterizerState(); +} + +void TextureCache::TCacheEntry::Bind(unsigned int stage) +{ + D3D::gfxstate->SetShaderResource(stage, texture->GetSRV()); +} + +TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, + unsigned int height, unsigned int expanded_width, + unsigned int tex_levels, PC_TexFormat pcfmt) +{ + D3D11_SUBRESOURCE_DATA srdata; + + D3D11_SUBRESOURCE_DATA *data = NULL; + D3D11_CPU_ACCESS_FLAG cpu_access = (D3D11_CPU_ACCESS_FLAG)0; + D3D11_USAGE usage = D3D11_USAGE_DEFAULT; + + // TODO: temp + tex_levels = 1; + + if (1 == tex_levels) + { + cpu_access = D3D11_CPU_ACCESS_WRITE; + usage = D3D11_USAGE_DYNAMIC; + + srdata.pSysMem = temp; + srdata.SysMemPitch = 4 * expanded_width; + data = &srdata; + } + + const D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, + width, height, 1, tex_levels, D3D11_BIND_SHADER_RESOURCE, usage, cpu_access); + + ID3D11Texture2D *pTexture; + HRESULT hr = D3D::device->CreateTexture2D(&texdesc, data, &pTexture); + CHECK(SUCCEEDED(hr), "Create texture of the TextureCache"); + + TCacheEntry* const entry = new TCacheEntry(new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE)); + entry->usage = usage; + + // TODO: silly debug names + D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetTex(), "a texture of the TextureCache"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetSRV(), "shader resource view of a texture of the TextureCache"); + + // wuts this? + //if (0 == tex_levels) + // PD3DX11FilterTexture(D3D::context, entry->texture->GetTex(), 0, D3DX11_DEFAULT); + + // TODO: this good? + //if (1 != tex_levels) + // entry->Load(width, height, expanded_width, 0); + + SAFE_RELEASE(pTexture); + + return entry; +} + +TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(int scaled_tex_w, int scaled_tex_h) +{ + return new TCacheEntry(D3DTexture2D::Create(scaled_tex_w, scaled_tex_h, + (D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET | (int)D3D11_BIND_SHADER_RESOURCE), + D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM)); +} + +TextureCache::TextureCache() +{ + HRESULT hr; + + D3D11_BLEND_DESC blenddesc; + blenddesc.AlphaToCoverageEnable = FALSE; + blenddesc.IndependentBlendEnable = FALSE; + blenddesc.RenderTarget[0].BlendEnable = FALSE; + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + hr = D3D::device->CreateBlendState(&blenddesc, &efbcopyblendstate); + CHECK(hr==S_OK, "Create blend state for CopyRenderTargetToTexture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyblendstate, "blend state used in CopyRenderTargetToTexture"); + + D3D11_DEPTH_STENCIL_DESC depthdesc; + depthdesc.DepthEnable = FALSE; + depthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + depthdesc.DepthFunc = D3D11_COMPARISON_LESS; + depthdesc.StencilEnable = FALSE; + depthdesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + depthdesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + hr = D3D::device->CreateDepthStencilState(&depthdesc, &efbcopydepthstate); + CHECK(hr==S_OK, "Create depth state for CopyRenderTargetToTexture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopydepthstate, "depth stencil state used in CopyRenderTargetToTexture"); + + D3D11_RASTERIZER_DESC rastdesc; + rastdesc.CullMode = D3D11_CULL_NONE; + rastdesc.FillMode = D3D11_FILL_SOLID; + rastdesc.FrontCounterClockwise = false; + rastdesc.DepthBias = false; + rastdesc.DepthBiasClamp = 0; + rastdesc.SlopeScaledDepthBias = 0; + rastdesc.DepthClipEnable = false; + rastdesc.ScissorEnable = false; + rastdesc.MultisampleEnable = false; + rastdesc.AntialiasedLineEnable = false; + hr = D3D::device->CreateRasterizerState(&rastdesc, &efbcopyraststate); + CHECK(hr==S_OK, "Create rasterizer state for CopyRenderTargetToTexture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyraststate, "rasterizer state used in CopyRenderTargetToTexture"); +} + +TextureCache::~TextureCache() +{ + SAFE_RELEASE(efbcopyblendstate); + SAFE_RELEASE(efbcopyraststate); + SAFE_RELEASE(efbcopydepthstate); + + for (unsigned int k = 0; k < 20; ++k) + SAFE_RELEASE(efbcopycbuf[k]); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_TextureCache.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_TextureCache.h new file mode 100644 index 0000000000..39433b7994 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_TextureCache.h @@ -0,0 +1,66 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +// VideoCommon +#include "VideoCommon.h" +#include "BPMemory.h" + +// DX11 +#include "DX11_D3DBase.h" +#include "DX11_D3DTexture.h" + +#include "../TextureCache.h" + +namespace DX11 +{ + +class TextureCache : public ::TextureCacheBase +{ +public: + struct TCacheEntry : TCacheEntryBase + { + D3DTexture2D* texture; + + D3D11_USAGE usage; + + TCacheEntry(D3DTexture2D* _texture) : texture(_texture) {} + ~TCacheEntry(); + + void Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level); + + void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect); + + void Bind(unsigned int stage); + }; + + TextureCache(); + ~TextureCache(); + + TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt); + + TCacheEntryBase* CreateRenderTargetTexture(int scaled_tex_w, int scaled_tex_h); + + void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, unsigned int bScaleByHalf, const EFBRectangle &source_rect); + +}; + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexManager.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexManager.cpp new file mode 100644 index 0000000000..2ad74f124f --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexManager.cpp @@ -0,0 +1,194 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +// Common +#include "Common.h" +#include "FileUtil.h" + +// VideoCommon +#include "BPStructs.h" +#include "XFStructs.h" +#include "Fifo.h" +#include "Statistics.h" +#include "Profiler.h" +#include "OpcodeDecoding.h" +#include "IndexGenerator.h" +#include "VertexShaderManager.h" +#include "PixelShaderManager.h" +#include "NativeVertexFormat.h" +#include "NativeVertexWriter.h" + +// DX11 +#include "DX11_D3DBase.h" +#include "DX11_D3DShader.h" +#include "DX11_D3DUtil.h" +#include "DX11_VertexManager.h" +#include "DX11_VertexShaderCache.h" +#include "DX11_PixelShaderCache.h" +#include "DX11_TextureCache.h" +#include "DX11_FramebufferManager.h" +#include "DX11_Render.h" + +#include "../Main.h" + +//using std::string; + +namespace DX11 +{ + +using namespace D3D; + +ID3D11Buffer* indexbuffers[NUM_INDEXBUFFERS] = {}; +ID3D11Buffer* vertexbuffers[NUM_VERTEXBUFFERS] = {}; + +// TODO: these seem ugly +inline ID3D11Buffer* GetSuitableIndexBuffer(const u32 minsize) +{ + for (u32 k = 0; k < NUM_INDEXBUFFERS - 1; ++k) + if (minsize > (((u32)MAXIBUFFERSIZE) >> k)) + return indexbuffers[k]; + return indexbuffers[NUM_INDEXBUFFERS - 1]; +} + +inline ID3D11Buffer* GetSuitableVertexBuffer(const u32 minsize) +{ + for (u32 k = 0; k < NUM_VERTEXBUFFERS - 1; ++k) + if (minsize > (((u32)MAXVBUFFERSIZE) >> (k + 1))) + return vertexbuffers[k]; + return vertexbuffers[NUM_VERTEXBUFFERS - 1]; +} + +void CreateDeviceObjects() +{ + D3D11_BUFFER_DESC bufdesc = CD3D11_BUFFER_DESC(MAXIBUFFERSIZE * 2, + D3D11_BIND_INDEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + for (u32 k = 0; k < NUM_INDEXBUFFERS; ++k, bufdesc.ByteWidth >>= 1) + { + CHECK(SUCCEEDED(D3D::device->CreateBuffer(&bufdesc, NULL, indexbuffers + k)), + "Failed to create index buffer [%i].", k); + D3D::SetDebugObjectName((ID3D11DeviceChild*)indexbuffers[k], "an index buffer of VertexManager"); + } + + bufdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bufdesc.ByteWidth = MAXVBUFFERSIZE; + for (u32 k = 0; k < NUM_VERTEXBUFFERS; ++k, bufdesc.ByteWidth >>= 1) + { + CHECK(SUCCEEDED(D3D::device->CreateBuffer(&bufdesc, NULL, vertexbuffers + k)), + "Failed to create vertex buffer [%i].", k); + D3D::SetDebugObjectName((ID3D11DeviceChild*)vertexbuffers[k], "a vertex buffer of VertexManager"); + } +} + +void DestroyDeviceObjects() +{ + for (u32 k = 0; k < NUM_INDEXBUFFERS; ++k) + SAFE_RELEASE(indexbuffers[k]); + + for (u32 k = 0; k < NUM_VERTEXBUFFERS; ++k) + SAFE_RELEASE(vertexbuffers[k]); +} + +VertexManager::VertexManager() +{ + CreateDeviceObjects(); +} + +VertexManager::~VertexManager() +{ + DestroyDeviceObjects(); + ResetBuffer(); +} + +void VertexManager::Draw(u32 stride, bool alphapass) +{ + static const UINT bufoffset = 0; + const UINT bufstride = stride; + + D3D11_MAPPED_SUBRESOURCE map; + ID3D11Buffer* const vertexbuffer = GetSuitableVertexBuffer((u32)(s_pCurBufferPointer - LocalVBuffer)); + + if (alphapass) + { + gfxstate->AlphaPass(); + } + else + { + context->Map(vertexbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, LocalVBuffer, (u32)(s_pCurBufferPointer - LocalVBuffer)); + context->Unmap(vertexbuffer, 0); + + gfxstate->ApplyState(); + } + + D3D::context->IASetVertexBuffers(0, 1, &vertexbuffer, &bufstride, &bufoffset); + + if (IndexGenerator::GetNumTriangles() > 0) + { + u32 indexbuffersize = IndexGenerator::GetTriangleindexLen(); + ID3D11Buffer* indexbuffer = GetSuitableIndexBuffer(2*indexbuffersize); + if (!alphapass) + { + D3D::context->Map(indexbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, TIBuffer, 2*indexbuffersize); + D3D::context->Unmap(indexbuffer, 0); + } + + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + D3D::context->IASetIndexBuffer(indexbuffer, DXGI_FORMAT_R16_UINT, 0); + + D3D::context->DrawIndexed(indexbuffersize, 0, 0); + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + if (IndexGenerator::GetNumLines() > 0) + { + u32 indexbuffersize = IndexGenerator::GetLineindexLen(); + ID3D11Buffer* indexbuffer = GetSuitableIndexBuffer(2*indexbuffersize); + if (!alphapass) + { + D3D::context->Map(indexbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, LIBuffer, 2*indexbuffersize); + D3D::context->Unmap(indexbuffer, 0); + } + + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); + D3D::context->IASetIndexBuffer(indexbuffer, DXGI_FORMAT_R16_UINT, 0); + + D3D::context->DrawIndexed(indexbuffersize, 0, 0); + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + if (IndexGenerator::GetNumPoints() > 0) + { + u32 indexbuffersize = IndexGenerator::GetPointindexLen(); + ID3D11Buffer* indexbuffer = GetSuitableIndexBuffer(2*indexbuffersize); + if (!alphapass) + { + D3D::context->Map(indexbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, PIBuffer, 2*indexbuffersize); + D3D::context->Unmap(indexbuffer, 0); + } + + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST); + D3D::context->IASetIndexBuffer(indexbuffer, DXGI_FORMAT_R16_UINT, 0); + + D3D::context->DrawIndexed(indexbuffersize, 0, 0); + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexManager.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexManager.h new file mode 100644 index 0000000000..3110976530 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexManager.h @@ -0,0 +1,49 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _DX11_VERTEXMANAGER_H +#define _DX11_VERTEXMANAGER_H + +#include "CPMemory.h" +#include "VertexLoader.h" + +#include "../VertexManager.h" + +namespace DX11 +{ + +enum +{ +// TODO: find sensible values for these two + NUM_VERTEXBUFFERS = 8, + NUM_INDEXBUFFERS = 10, +}; + +class VertexManager : public ::VertexManagerBase +{ +public: + VertexManager(); + ~VertexManager(); + + void Draw(u32 stride, bool alphapass); + + NativeVertexFormat* CreateNativeVertexFormat(); +}; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexShaderCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexShaderCache.cpp new file mode 100644 index 0000000000..c932758344 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexShaderCache.cpp @@ -0,0 +1,283 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +// Common +#include "Common.h" +#include "FileUtil.h" +#include "LinearDiskCache.h" + +// VideoCommon +#include "VideoConfig.h" +#include "Statistics.h" +#include "Profiler.h" +#include "VertexLoader.h" +#include "BPMemory.h" +#include "XFMemory.h" + +// DX11 +#include "DX11_D3DBase.h" +#include "DX11_D3DShader.h" +#include "DX11_VertexShaderCache.h" +#include "DX11_Render.h" + +#include "../Main.h" + +namespace DX11 +{ + +VertexShaderCache::VSCache VertexShaderCache::vshaders; +const VertexShaderCache::VSCacheEntry* VertexShaderCache::last_entry; + +static ID3D11VertexShader* SimpleVertexShader = NULL; +static ID3D11VertexShader* ClearVertexShader = NULL; +static ID3D11InputLayout* SimpleLayout = NULL; +static ID3D11InputLayout* ClearLayout = NULL; + +LinearDiskCache g_vs_disk_cache; + +ID3D11VertexShader* VertexShaderCache::GetSimpleVertexShader() { return SimpleVertexShader; } +ID3D11VertexShader* VertexShaderCache::GetClearVertexShader() { return ClearVertexShader; } +ID3D11InputLayout* VertexShaderCache::GetSimpleInputLayout() { return SimpleLayout; } +ID3D11InputLayout* VertexShaderCache::GetClearInputLayout() { return ClearLayout; } + +// maps the constant numbers to float indices in the constant buffer +unsigned int vs_constant_offset_table[C_VENVCONST_END]; +void VertexShaderCache::SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number] ] = f1; + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]+1] = f2; + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]+2] = f3; + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]+3] = f4; + D3D::gfxstate->vscbufchanged = true; +} + +void VertexShaderCache::SetVSConstant4fv(unsigned int const_number, const float* f) +{ + memcpy(&D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]], f, sizeof(float)*4); + D3D::gfxstate->vscbufchanged = true; +} + +void VertexShaderCache::SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float* f) +{ + for (unsigned int i = 0; i < count; i++) + { + memcpy(&D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number+i]], f+3*i, sizeof(float)*3); + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number+i]+3] = 0.f; + } + D3D::gfxstate->vscbufchanged = true; +} + +void VertexShaderCache::SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float* f) +{ + memcpy(&D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]], f, sizeof(float)*4*count); + D3D::gfxstate->vscbufchanged = true; +} + +// this class will load the precompiled shaders into our cache +class VertexShaderCacheInserter : public LinearDiskCacheReader { +public: + void Read(const u8* key, int key_size, const u8* value, int value_size) + { + VERTEXSHADERUID uid; + if (key_size != sizeof(uid)) + { + ERROR_LOG(VIDEO, "Wrong key size in vertex shader cache"); + return; + } + memcpy(&uid, key, key_size); + + D3DBlob* blob = new D3DBlob(value_size, value); + VertexShaderCache::InsertByteCode(uid, blob); + blob->Release(); + } +}; + +const char simple_shader_code[] = { + "struct VSOUTPUT\n" + "{\n" + "float4 vPosition : POSITION;\n" + "float2 vTexCoord : TEXCOORD0;\n" + "};\n" + "VSOUTPUT main(float4 inPosition : POSITION,float2 inTEX0 : TEXCOORD0)\n" + "{\n" + "VSOUTPUT OUT;\n" + "OUT.vPosition = inPosition;\n" + "OUT.vTexCoord = inTEX0;\n" + "return OUT;\n" + "}\n" +}; + +const char clear_shader_code[] = { + "struct VSOUTPUT\n" + "{\n" + "float4 vPosition : POSITION;\n" + "float4 vColor0 : COLOR0;\n" + "};\n" + "VSOUTPUT main(float4 inPosition : POSITION,float4 inColor0: COLOR0)\n" + "{\n" + "VSOUTPUT OUT;\n" + "OUT.vPosition = inPosition;\n" + "OUT.vColor0 = inColor0;\n" + "return OUT;\n" + "}\n" +}; + +VertexShaderCache::VertexShaderCache() +{ + const D3D11_INPUT_ELEMENT_DESC simpleelems[2] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + const D3D11_INPUT_ELEMENT_DESC clearelems[2] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + + D3DBlob* blob; + D3D::CompileVertexShader(simple_shader_code, sizeof(simple_shader_code), &blob); + D3D::device->CreateInputLayout(simpleelems, 2, blob->Data(), blob->Size(), &SimpleLayout); + SimpleVertexShader = D3D::CreateVertexShaderFromByteCode(blob); + if (SimpleLayout == NULL || SimpleVertexShader == NULL) PanicAlert("Failed to create simple vertex shader or input layout at %s %d\n", __FILE__, __LINE__); + blob->Release(); + D3D::SetDebugObjectName((ID3D11DeviceChild*)SimpleVertexShader, "simple vertex shader"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)SimpleLayout, "simple input layout"); + + D3D::CompileVertexShader(clear_shader_code, sizeof(clear_shader_code), &blob); + D3D::device->CreateInputLayout(clearelems, 2, blob->Data(), blob->Size(), &ClearLayout); + ClearVertexShader = D3D::CreateVertexShaderFromByteCode(blob); + if (ClearLayout == NULL || ClearVertexShader == NULL) PanicAlert("Failed to create clear vertex shader or input layout at %s %d\n", __FILE__, __LINE__); + blob->Release(); + D3D::SetDebugObjectName((ID3D11DeviceChild*)ClearVertexShader, "clear vertex shader"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)ClearLayout, "clear input layout"); + + Clear(); + + // these values are hardcoded, they depend on internal D3DCompile behavior + // TODO: Do this with D3DReflect or something instead + unsigned int k; + for (k = 0;k < 6;k++) vs_constant_offset_table[C_POSNORMALMATRIX+k] = 0+4*k; + for (k = 0;k < 4;k++) vs_constant_offset_table[C_PROJECTION+k] = 24+4*k; + for (k = 0;k < 4;k++) vs_constant_offset_table[C_MATERIALS+k] = 40+4*k; + for (k = 0;k < 40;k++) vs_constant_offset_table[C_LIGHTS+k] = 56+4*k; + for (k = 0;k < 24;k++) vs_constant_offset_table[C_TEXMATRICES+k] = 216+4*k; + for (k = 0;k < 64;k++) vs_constant_offset_table[C_TRANSFORMMATRICES+k] = 312+4*k; + for (k = 0;k < 32;k++) vs_constant_offset_table[C_NORMALMATRICES+k] = 568+4*k; + for (k = 0;k < 64;k++) vs_constant_offset_table[C_POSTTRANSFORMMATRICES+k] = 696+4*k; + for (k = 0;k < 4;k++) vs_constant_offset_table[C_DEPTHPARAMS+k] = 952+4*k; + + if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) + File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); + + char cache_filename[MAX_PATH]; + sprintf(cache_filename, "%sdx11-%s-vs.cache", File::GetUserPath(D_SHADERCACHE_IDX), g_globals->unique_id); + VertexShaderCacheInserter inserter; + g_vs_disk_cache.OpenAndRead(cache_filename, &inserter); +} + +void VertexShaderCache::Clear() +{ + for (VSCache::iterator iter = vshaders.begin(); iter != vshaders.end(); ++iter) + iter->second.Destroy(); + vshaders.clear(); +} + +VertexShaderCache::~VertexShaderCache() +{ + SAFE_RELEASE(SimpleVertexShader); + SAFE_RELEASE(ClearVertexShader); + + SAFE_RELEASE(SimpleLayout); + SAFE_RELEASE(ClearLayout); + + Clear(); + g_vs_disk_cache.Sync(); + g_vs_disk_cache.Close(); +} + +bool VertexShaderCache::SetShader(u32 components) +{ + DVSTARTPROFILE(); + + VERTEXSHADERUID uid; + GetVertexShaderId(&uid, components); + if (uid == last_vertex_shader_uid && vshaders[uid].frameCount == frameCount) + return (vshaders[uid].shader != NULL); + + memcpy(&last_vertex_shader_uid, &uid, sizeof(VERTEXSHADERUID)); + + VSCache::iterator iter; + iter = vshaders.find(uid); + if (iter != vshaders.end()) + { + iter->second.frameCount = frameCount; + const VSCacheEntry &entry = iter->second; + last_entry = &entry; + + if (entry.shader) D3D::gfxstate->SetVShader(entry.shader, iter->second.bytecode); + return (entry.shader != NULL); + } + + const char* code = GenerateVertexShaderCode(components, API_D3D11); + + D3DBlob* pbytecode = NULL; + D3D::CompileVertexShader(code, (int)strlen(code), &pbytecode); + + if (pbytecode == NULL) + { + PanicAlert("Failed to compile Vertex Shader %s %d:\n\n%s", __FILE__, __LINE__, code); + return false; + } + g_vs_disk_cache.Append((u8*)&uid, sizeof(uid), (const u8*)pbytecode->Data(), pbytecode->Size()); + g_vs_disk_cache.Sync(); + + bool result = InsertByteCode(uid, pbytecode); + D3D::gfxstate->SetVShader(last_entry->shader, last_entry->bytecode); + pbytecode->Release(); + return result; +} + +bool VertexShaderCache::InsertByteCode(const VERTEXSHADERUID &uid, D3DBlob* bcodeblob) +{ + ID3D11VertexShader* shader = D3D::CreateVertexShaderFromByteCode(bcodeblob); + if (shader == NULL) + { + PanicAlert("Failed to create vertex shader from %p size %d at %s %d\n", bcodeblob->Data(), bcodeblob->Size(), __FILE__, __LINE__); + return false; + } + // TODO: Somehow make the debug name a bit more specific + D3D::SetDebugObjectName((ID3D11DeviceChild*)shader, "a vertex shader of VertexShaderCache"); + + // Make an entry in the table + VSCacheEntry entry; + entry.shader = shader; + entry.frameCount = frameCount; + entry.SetByteCode(bcodeblob); + + vshaders[uid] = entry; + last_entry = &vshaders[uid]; + + INCSTAT(stats.numVertexShadersCreated); + SETSTAT(stats.numVertexShadersAlive, (int)vshaders.size()); + + return true; +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexShaderCache.h b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexShaderCache.h new file mode 100644 index 0000000000..fe82b138eb --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX11/DX11_VertexShaderCache.h @@ -0,0 +1,80 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _DX11_VERTEXSHADERCACHE_H +#define _DX11_VERTEXSHADERCACHE_H + +#include + +#include "VertexShaderGen.h" +#include "DX11_D3DBase.h" + +#include "../VertexShaderCache.h" + +namespace DX11 +{ + +class VertexShaderCache : public ::VertexShaderCacheBase +{ +public: + VertexShaderCache(); + ~VertexShaderCache(); + + static void Clear(); + bool SetShader(u32 components); + + static ID3D11VertexShader* GetSimpleVertexShader(); + static ID3D11VertexShader* GetClearVertexShader(); + static ID3D11InputLayout* GetSimpleInputLayout(); + static ID3D11InputLayout* GetClearInputLayout(); + + static bool VertexShaderCache::InsertByteCode(const VERTEXSHADERUID &uid, D3DBlob* bcodeblob); + + void SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4); + void SetVSConstant4fv(unsigned int const_number, const float* f); + void SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float* f); + void SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float* f); + +private: + struct VSCacheEntry + { + ID3D11VertexShader* shader; + D3DBlob* bytecode; // needed to initialize the input layout + int frameCount; + + VSCacheEntry() : shader(NULL), bytecode(NULL), frameCount(0) {} + void SetByteCode(D3DBlob* blob) + { + SAFE_RELEASE(bytecode); + bytecode = blob; + blob->AddRef(); + } + void Destroy() + { + SAFE_RELEASE(shader); + SAFE_RELEASE(bytecode); + } + }; + typedef std::map VSCache; + + static VSCache vshaders; + static const VSCacheEntry* last_entry; +}; + +} + +#endif // _VERTEXSHADERCACHE_H diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DBase.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DBase.cpp new file mode 100644 index 0000000000..a6d54ca7e1 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DBase.cpp @@ -0,0 +1,730 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "DX9_D3DBase.h" +#include "VideoConfig.h" +#include "DX9_Render.h" +#include "XFStructs.h" +#include "StringUtil.h" + +#pragma comment(lib, "d3d9.lib") + +namespace DX9 +{ + +// D3DX +HINSTANCE hD3DXDll = NULL; +D3DXSAVESURFACETOFILEATYPE PD3DXSaveSurfaceToFileA = NULL; +D3DXSAVETEXTURETOFILEATYPE PD3DXSaveTextureToFileA = NULL; +D3DXCOMPILESHADERTYPE PD3DXCompileShader = NULL; + +namespace D3D +{ + +LPDIRECT3D9 D3D = NULL; // Used to create the D3DDevice +LPDIRECT3DDEVICE9 dev = NULL; // Our rendering device +LPDIRECT3DSURFACE9 back_buffer; +LPDIRECT3DSURFACE9 back_buffer_z; +D3DCAPS9 caps; +HWND hWnd; + +static int multisample; +static int resolution; +static int xres, yres; +static bool auto_depth_stencil = false; + +#define VENDOR_NVIDIA 4318 +#define VENDOR_ATI 4098 + +bool bFrameInProgress = false; + +#define MAX_ADAPTERS 4 +static Adapter adapters[MAX_ADAPTERS]; +static int numAdapters; +static int cur_adapter; + +// Value caches for state filtering +const int MaxTextureStages = 9; +const int MaxRenderStates = 210 + 46; +const int MaxTextureTypes = 33; +const int MaxSamplerSize = 13; +const int MaxSamplerTypes = 15; +static bool m_RenderStatesSet[MaxRenderStates]; +static DWORD m_RenderStates[MaxRenderStates]; +static bool m_RenderStatesChanged[MaxRenderStates]; + +static DWORD m_TextureStageStates[MaxTextureStages][MaxTextureTypes]; +static bool m_TextureStageStatesSet[MaxTextureStages][MaxTextureTypes]; +static bool m_TextureStageStatesChanged[MaxTextureStages][MaxTextureTypes]; + +static DWORD m_SamplerStates[MaxSamplerSize][MaxSamplerTypes]; +static bool m_SamplerStatesSet[MaxSamplerSize][MaxSamplerTypes]; +static bool m_SamplerStatesChanged[MaxSamplerSize][MaxSamplerTypes]; + +LPDIRECT3DBASETEXTURE9 m_Textures[16]; +LPDIRECT3DVERTEXDECLARATION9 m_VtxDecl; +LPDIRECT3DPIXELSHADER9 m_PixelShader; +LPDIRECT3DVERTEXSHADER9 m_VertexShader; + +void Enumerate(); + +int GetNumAdapters() { return numAdapters; } +const Adapter &GetAdapter(int i) { return adapters[i]; } +const Adapter &GetCurAdapter() { return adapters[cur_adapter]; } + +bool IsATIDevice() +{ + return GetCurAdapter().ident.VendorId == VENDOR_ATI; +} + + +HRESULT Init() +{ + // Create the D3D object, which is needed to create the D3DDevice. + D3D = Direct3DCreate9(D3D_SDK_VERSION); + if (!D3D) + return E_FAIL; + Enumerate(); + return S_OK; +} + +void Shutdown() +{ + D3D->Release(); + D3D = 0; +} + +void EnableAlphaToCoverage() +{ + // Each vendor has their own specific little hack. + if (GetCurAdapter().ident.VendorId == VENDOR_ATI) + D3D::SetRenderState(D3DRS_POINTSIZE, (D3DFORMAT)MAKEFOURCC('A', '2', 'M', '1')); + else + D3D::SetRenderState(D3DRS_ADAPTIVETESS_Y, (D3DFORMAT)MAKEFOURCC('A', 'T', 'O', 'C')); +} + +void InitPP(int adapter, int f, int aa_mode, D3DPRESENT_PARAMETERS *pp) +{ + ZeroMemory(pp, sizeof(D3DPRESENT_PARAMETERS)); + pp->hDeviceWindow = hWnd; + + if (auto_depth_stencil) + { + pp->EnableAutoDepthStencil = TRUE; + pp->AutoDepthStencilFormat = D3DFMT_D24S8; + } + else + { + pp->EnableAutoDepthStencil = FALSE; + pp->AutoDepthStencilFormat = D3DFMT_UNKNOWN; + } + + pp->BackBufferFormat = D3DFMT_X8R8G8B8; + if (aa_mode >= (int)adapters[adapter].aa_levels.size()) + aa_mode = 0; + + pp->MultiSampleType = adapters[adapter].aa_levels[aa_mode].ms_setting; + pp->MultiSampleQuality = adapters[adapter].aa_levels[aa_mode].qual_setting; + + pp->Flags = auto_depth_stencil ? D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL : 0; + + RECT client; + GetClientRect(hWnd, &client); + xres = pp->BackBufferWidth = client.right - client.left; + yres = pp->BackBufferHeight = client.bottom - client.top; + pp->SwapEffect = D3DSWAPEFFECT_DISCARD; + pp->PresentationInterval = g_Config.bVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE; + pp->Windowed = TRUE; +} + +void Enumerate() +{ + numAdapters = D3D::D3D->GetAdapterCount(); + + for (int i = 0; i < std::min(MAX_ADAPTERS, numAdapters); i++) + { + Adapter &a = adapters[i]; + a.aa_levels.clear(); + a.resolutions.clear(); + D3D::D3D->GetAdapterIdentifier(i, 0, &a.ident); + bool isNvidia = a.ident.VendorId == VENDOR_NVIDIA; + + // Add SuperSamples modes + a.aa_levels.push_back(AALevel("None", D3DMULTISAMPLE_NONE, 0)); + a.aa_levels.push_back(AALevel("4x SSAA", D3DMULTISAMPLE_NONE, 0)); + a.aa_levels.push_back(AALevel("9x SSAA", D3DMULTISAMPLE_NONE, 0)); + //Add multisample modes + //disable them will they are not implemnted + /* + DWORD qlevels = 0; + if (D3DERR_NOTAVAILABLE != D3D::D3D->CheckDeviceMultiSampleType( + i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, TRUE, D3DMULTISAMPLE_2_SAMPLES, &qlevels)) + if (qlevels > 0) + a.aa_levels.push_back(AALevel("2x MSAA", D3DMULTISAMPLE_2_SAMPLES, 0)); + + if (D3DERR_NOTAVAILABLE != D3D::D3D->CheckDeviceMultiSampleType( + i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, TRUE, D3DMULTISAMPLE_2_SAMPLES, &qlevels)) + if (qlevels > 0) + a.aa_levels.push_back(AALevel("4x MSAA", D3DMULTISAMPLE_4_SAMPLES, 0)); + + if (D3DERR_NOTAVAILABLE != D3D::D3D->CheckDeviceMultiSampleType( + i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, TRUE, D3DMULTISAMPLE_8_SAMPLES, &qlevels)) + if (qlevels > 0) + a.aa_levels.push_back(AALevel("8x MSAA", D3DMULTISAMPLE_8_SAMPLES, 0)); + + if (isNvidia) + { + // CSAA support + if (D3DERR_NOTAVAILABLE != D3D::D3D->CheckDeviceMultiSampleType( + i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, TRUE, D3DMULTISAMPLE_4_SAMPLES, &qlevels)) + { + if (qlevels > 2) + { + // 8x, 8xQ are available + // See http://developer.nvidia.com/object/coverage-sampled-aa.html + a.aa_levels.push_back(AALevel("8x CSAA", D3DMULTISAMPLE_4_SAMPLES, 2)); + a.aa_levels.push_back(AALevel("8xQ CSAA", D3DMULTISAMPLE_8_SAMPLES, 0)); + } + } + if (D3DERR_NOTAVAILABLE != D3D::D3D->CheckDeviceMultiSampleType( + i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, TRUE, D3DMULTISAMPLE_8_SAMPLES, &qlevels)) + { + if (qlevels > 2) + { + // 16x, 16xQ are available + // See http://developer.nvidia.com/object/coverage-sampled-aa.html + a.aa_levels.push_back(AALevel("16x CSAA", D3DMULTISAMPLE_4_SAMPLES, 4)); + a.aa_levels.push_back(AALevel("16xQ CSAA", D3DMULTISAMPLE_8_SAMPLES, 2)); + } + } + } + */ + // Determine if INTZ is supported. Code from ATI's doc. + // http://developer.amd.com/gpu_assets/Advanced%20DX9%20Capabilities%20for%20ATI%20Radeon%20Cards.pdf + a.supports_intz = D3D_OK == D3D->CheckDeviceFormat( + i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, FOURCC_INTZ); + // Also check for RAWZ (nvidia only, but the only option to get Z24 textures on sub GF8800 + a.supports_rawz = D3D_OK == D3D->CheckDeviceFormat( + i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, FOURCC_RAWZ); + // Might as well check for RESZ and NULL too. + a.supports_resz = D3D_OK == D3D->CheckDeviceFormat( + i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, FOURCC_RESZ); + a.supports_null = D3D_OK == D3D->CheckDeviceFormat( + i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, FOURCC_NULL); + + if (a.aa_levels.size() == 1) + { + strcpy(a.aa_levels[0].name, "(Not supported on this device)"); + } + int numModes = D3D::D3D->GetAdapterModeCount(i, D3DFMT_X8R8G8B8); + for (int m = 0; m < numModes; m++) + { + D3DDISPLAYMODE mode; + D3D::D3D->EnumAdapterModes(i, D3DFMT_X8R8G8B8, m, &mode); + + int found = -1; + for (int x = 0; x < (int)a.resolutions.size(); x++) + { + if (a.resolutions[x].xres == mode.Width && a.resolutions[x].yres == mode.Height) + { + found = x; + break; + } + } + + Resolution temp; + Resolution &r = found==-1 ? temp : a.resolutions[found]; + + sprintf(r.name, "%ix%i", mode.Width, mode.Height); + r.bitdepths.insert(mode.Format); + r.refreshes.insert(mode.RefreshRate); + if (found == -1 && mode.Width >= 640 && mode.Height >= 480) + { + r.xres = mode.Width; + r.yres = mode.Height; + a.resolutions.push_back(r); + } + } + } +} + +// dynamically picks one of the available d3dx9 dlls and loads it. +// we're first trying to load the dll Dolphin was compiled with, otherwise the most up-to-date one +HRESULT LoadD3DX9() +{ + HRESULT hr = E_FAIL; + hD3DXDll = LoadLibraryA(StringFromFormat("d3dx9_%d.dll", D3DX_SDK_VERSION).c_str()); + if (hD3DXDll != NULL) + { + NOTICE_LOG(VIDEO, "Successfully loaded %s.", StringFromFormat("d3dx9_%d.dll", D3DX_SDK_VERSION).c_str()); + hr = S_OK; + } + else + { + // if that fails, try loading older dlls (no need to look for newer ones) + for (unsigned int num = D3DX_SDK_VERSION-1; num >= 24; --num) + { + hD3DXDll = LoadLibraryA(StringFromFormat("d3dx9_%d.dll", num).c_str()); + if (hD3DXDll != NULL) + { + NOTICE_LOG(VIDEO, "Successfully loaded %s. If you're having trouble, try updating your DX runtime first.", StringFromFormat("d3dx9_%d.dll", num).c_str()); + hr = S_OK; + break; + } + } + if (FAILED(hr)) + { + MessageBoxA(NULL, "Failed to load any D3DX9 dll, update your DX9 runtime, please", "Critical error", MB_OK | MB_ICONERROR); + return hr; + } + } + PD3DXCompileShader = (D3DXCOMPILESHADERTYPE)GetProcAddress(hD3DXDll, "D3DXCompileShader"); + if (PD3DXCompileShader == NULL) + { + MessageBoxA(NULL, "GetProcAddress failed for D3DXCompileShader!", "Critical error", MB_OK | MB_ICONERROR); + goto fail; + } + + PD3DXSaveSurfaceToFileA = (D3DXSAVESURFACETOFILEATYPE)GetProcAddress(hD3DXDll, "D3DXSaveSurfaceToFileA"); + if (PD3DXSaveSurfaceToFileA == NULL) + { + MessageBoxA(NULL, "GetProcAddress failed for D3DXSaveSurfaceToFileA!", "Critical error", MB_OK | MB_ICONERROR); + goto fail; + } + + PD3DXSaveTextureToFileA = (D3DXSAVETEXTURETOFILEATYPE)GetProcAddress(hD3DXDll, "D3DXSaveTextureToFileA"); + if (PD3DXSaveTextureToFileA == NULL) + { + MessageBoxA(NULL, "GetProcAddress failed for D3DXSaveTextureToFileA!", "Critical error", MB_OK | MB_ICONERROR); + goto fail; + } + return S_OK; + +fail: + FreeLibrary(hD3DXDll); + PD3DXCompileShader = NULL; + PD3DXSaveSurfaceToFileA = NULL; + PD3DXSaveTextureToFileA = NULL; + return E_FAIL; +} + +void UnloadD3DX9() +{ + FreeLibrary(hD3DXDll); + PD3DXCompileShader = NULL; + PD3DXSaveSurfaceToFileA = NULL; + PD3DXSaveTextureToFileA = NULL; +} + +HRESULT Create(int adapter, HWND wnd, int _resolution, int aa_mode, bool auto_depth) +{ + hWnd = wnd; + multisample = aa_mode; + resolution = _resolution; + auto_depth_stencil = auto_depth; + cur_adapter = adapter; + D3DPRESENT_PARAMETERS d3dpp; + + HRESULT hr = LoadD3DX9(); + if (FAILED(hr)) return hr; + + InitPP(adapter, resolution, aa_mode, &d3dpp); + + if (FAILED(D3D->CreateDevice( + adapter, + D3DDEVTYPE_HAL, + wnd, + D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE, //doesn't seem to make a difference + &d3dpp, &dev))) + { + if (FAILED(D3D->CreateDevice( + adapter, + D3DDEVTYPE_HAL, + wnd, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, + &d3dpp, &dev))) + { + MessageBox(wnd, + _T("Failed to initialize Direct3D."), + _T("Dolphin Direct3D plugin"), MB_OK | MB_ICONERROR); + return E_FAIL; + } + } + + dev->GetDeviceCaps(&caps); + dev->GetRenderTarget(0, &back_buffer); + if (dev->GetDepthStencilSurface(&back_buffer_z) == D3DERR_NOTFOUND) + back_buffer_z = NULL; + D3D::SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE ); + D3D::SetRenderState(D3DRS_FILLMODE, g_Config.bWireFrame ? D3DFILL_WIREFRAME : D3DFILL_SOLID); + memset(m_Textures, 0, sizeof(m_Textures)); + memset(m_TextureStageStatesSet, 0, sizeof(m_TextureStageStatesSet)); + memset(m_RenderStatesSet, 0, sizeof(m_RenderStatesSet)); + memset(m_SamplerStatesSet, 0, sizeof(m_SamplerStatesSet)); + memset(m_TextureStageStatesChanged, 0, sizeof(m_TextureStageStatesChanged)); + memset(m_RenderStatesChanged, 0, sizeof(m_RenderStatesChanged)); + memset(m_SamplerStatesChanged, 0, sizeof(m_SamplerStatesChanged)); + m_VtxDecl = NULL; + m_PixelShader = NULL; + m_VertexShader = NULL; + // Device state would normally be set here + return S_OK; +} + +void Close() +{ + UnloadD3DX9(); + + if (back_buffer_z) + back_buffer_z->Release(); + back_buffer_z = NULL; + if( back_buffer ) + back_buffer->Release(); + back_buffer = NULL; + + ULONG references = dev->Release(); + if (references) + ERROR_LOG(VIDEO, "Unreleased references: %i.", references); + + dev = NULL; +} + +const D3DCAPS9 &GetCaps() +{ + return caps; +} + +const char *VertexShaderVersionString() +{ + static const char *versions[5] = {"ERROR", "vs_1_4", "vs_2_0", "vs_3_0", "vs_4_0"}; + int version = ((D3D::caps.VertexShaderVersion >> 8) & 0xFF); + return versions[std::min(4, version)]; +} + +const char *PixelShaderVersionString() +{ + static const char *versions[5] = {"ERROR", "ps_1_4", "ps_2_0", "ps_3_0", "ps_4_0"}; + int version = ((D3D::caps.PixelShaderVersion >> 8) & 0xFF); + return versions[std::min(4, version)]; +} + +LPDIRECT3DSURFACE9 GetBackBufferSurface() +{ + return back_buffer; +} + +LPDIRECT3DSURFACE9 GetBackBufferDepthSurface() +{ + return back_buffer_z; +} + +void ShowD3DError(HRESULT err) +{ + switch (err) + { + case D3DERR_DEVICELOST: + PanicAlert("Device Lost"); + break; + case D3DERR_INVALIDCALL: + PanicAlert("Invalid Call"); + break; + case D3DERR_DRIVERINTERNALERROR: + PanicAlert("Driver Internal Error"); + break; + case D3DERR_OUTOFVIDEOMEMORY: + PanicAlert("Out of vid mem"); + break; + default: + // MessageBox(0,_T("Other error or success"),_T("ERROR"),0); + break; + } +} + +void Reset() +{ + if (dev) + { + // ForgetCachedState(); + + // Can't keep a pointer around to the backbuffer surface when resetting. + SAFE_RELEASE(back_buffer_z); + SAFE_RELEASE(back_buffer); + + D3DPRESENT_PARAMETERS d3dpp; + InitPP(cur_adapter, resolution, multisample, &d3dpp); + HRESULT hr = dev->Reset(&d3dpp); + ShowD3DError(hr); + + dev->GetRenderTarget(0, &back_buffer); + if (dev->GetDepthStencilSurface(&back_buffer_z) == D3DERR_NOTFOUND) + back_buffer_z = NULL; + ApplyCachedState(); + } +} + +int GetBackBufferWidth() +{ + return xres; +} + +int GetBackBufferHeight() +{ + return yres; +} + +bool BeginFrame() +{ + if (bFrameInProgress) + { + PanicAlert("BeginFrame WTF"); + return false; + } + bFrameInProgress = true; + if (dev) + { + dev->BeginScene(); + return true; + } + else + return false; +} + +void EndFrame() +{ + if (!bFrameInProgress) + { + PanicAlert("EndFrame WTF"); + return; + } + bFrameInProgress = false; + dev->EndScene(); +} + +void Present() +{ + if (dev) + { + dev->Present(NULL, NULL, NULL, NULL); + } +} + +void ApplyCachedState() +{ + for (int sampler = 0; sampler < 8; sampler++) + { + for (int type = 0; type < MaxSamplerTypes; type++) + { + if(m_SamplerStatesSet[sampler][type]) + D3D::dev->SetSamplerState(sampler, (D3DSAMPLERSTATETYPE)type, m_SamplerStates[sampler][type]); + } + } + + for (int rs = 0; rs < MaxRenderStates; rs++) + { + if (m_RenderStatesSet[rs]) + D3D::dev->SetRenderState((D3DRENDERSTATETYPE)rs, m_RenderStates[rs]); + } + + // We don't bother restoring these so let's just wipe the state copy + // so no stale state is around. + memset(m_Textures, 0, sizeof(m_Textures)); + memset(m_TextureStageStatesSet, 0, sizeof(m_TextureStageStatesSet)); + memset(m_TextureStageStatesChanged, 0, sizeof(m_TextureStageStatesChanged)); + m_VtxDecl = NULL; + m_PixelShader = NULL; + m_VertexShader = NULL; +} + +void SetTexture(DWORD Stage, LPDIRECT3DBASETEXTURE9 pTexture) +{ + if (m_Textures[Stage] != pTexture) + { + m_Textures[Stage] = pTexture; + D3D::dev->SetTexture(Stage, pTexture); + } +} + +void RefreshRenderState(D3DRENDERSTATETYPE State) +{ + if(m_RenderStatesSet[State] && m_RenderStatesChanged[State]) + { + D3D::dev->SetRenderState(State, m_RenderStates[State]); + m_RenderStatesChanged[State] = false; + } +} + +void SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) +{ + if (m_RenderStates[State] != Value || !m_RenderStatesSet[State]) + { + m_RenderStates[State] = Value; + m_RenderStatesSet[State] = true; + m_RenderStatesChanged[State] = false; + D3D::dev->SetRenderState(State, Value); + } +} + +void ChangeRenderState(D3DRENDERSTATETYPE State, DWORD Value) +{ + if (m_RenderStates[State] != Value || !m_RenderStatesSet[State]) + { + m_RenderStatesChanged[State] = m_RenderStatesSet[State]; + D3D::dev->SetRenderState(State, Value); + } + else + { + m_RenderStatesChanged[State] = false; + } +} + +void SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) +{ + if (m_TextureStageStates[Stage][Type] != Value || !m_TextureStageStatesSet[Stage][Type]) + { + m_TextureStageStates[Stage][Type] = Value; + m_TextureStageStatesSet[Stage][Type]=true; + m_TextureStageStatesChanged[Stage][Type]=false; + D3D::dev->SetTextureStageState(Stage, Type, Value); + } +} + +void RefreshTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type) +{ + if(m_TextureStageStatesSet[Stage][Type] && m_TextureStageStatesChanged[Stage][Type]) + { + D3D::dev->SetTextureStageState(Stage, Type, m_TextureStageStates[Stage][Type]); + m_TextureStageStatesChanged[Stage][Type] = false; + } +} + +void ChangeTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) +{ + if (m_TextureStageStates[Stage][Type] != Value || !m_TextureStageStatesSet[Stage][Type]) + { + m_TextureStageStatesChanged[Stage][Type] = m_TextureStageStatesSet[Stage][Type]; + D3D::dev->SetTextureStageState(Stage, Type, Value); + } + else + { + m_TextureStageStatesChanged[Stage][Type] = false; + } +} + +void SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) +{ + if (m_SamplerStates[Sampler][Type] != Value || !m_SamplerStatesSet[Sampler][Type]) + { + m_SamplerStates[Sampler][Type] = Value; + m_SamplerStatesSet[Sampler][Type] = true; + m_SamplerStatesChanged[Sampler][Type] = false; + D3D::dev->SetSamplerState(Sampler, Type, Value); + } +} + +void RefreshSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type) +{ + if(m_SamplerStatesSet[Sampler][Type] && m_SamplerStatesChanged[Sampler][Type]) + { + D3D::dev->SetSamplerState(Sampler, Type, m_SamplerStates[Sampler][Type]); + m_SamplerStatesChanged[Sampler][Type] = false; + } +} + +void ChangeSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) +{ + if (m_SamplerStates[Sampler][Type] != Value || !m_SamplerStatesSet[Sampler][Type]) + { + m_SamplerStatesChanged[Sampler][Type] = m_SamplerStatesSet[Sampler][Type]; + D3D::dev->SetSamplerState(Sampler, Type, Value); + } + else + { + m_SamplerStatesChanged[Sampler][Type] = false; + } +} + +void RefreshVertexDeclaration() +{ + if (m_VtxDecl) + { + D3D::dev->SetVertexDeclaration(m_VtxDecl); + } +} + +void SetVertexDeclaration(LPDIRECT3DVERTEXDECLARATION9 decl) +{ + if (!decl) { + m_VtxDecl = NULL; + return; + } + if (decl != m_VtxDecl) + { + D3D::dev->SetVertexDeclaration(decl); + m_VtxDecl = decl; + } +} + +void RefreshVertexShader() +{ + if (m_VertexShader) + { + D3D::dev->SetVertexShader(m_VertexShader); + } +} + +void SetVertexShader(LPDIRECT3DVERTEXSHADER9 shader) +{ + if (!shader) { + m_VertexShader = NULL; + return; + } + if (shader != m_VertexShader) + { + D3D::dev->SetVertexShader(shader); + m_VertexShader = shader; + } +} + +void RefreshPixelShader() +{ + if (m_PixelShader) + { + D3D::dev->SetPixelShader(m_PixelShader); + } +} + +void SetPixelShader(LPDIRECT3DPIXELSHADER9 shader) +{ + if (!shader) { + m_PixelShader = NULL; + return; + } + if (shader != m_PixelShader) + { + D3D::dev->SetPixelShader(shader); + m_PixelShader = shader; + } +} + + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DBase.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DBase.h new file mode 100644 index 0000000000..8ebdc47de0 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DBase.h @@ -0,0 +1,165 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _D3DBASE_H +#define _D3DBASE_H + +#include +#include + +#include + +#include "Common.h" + +namespace DX9 +{ + +namespace D3D +{ + +#define SAFE_RELEASE(x) { if (x) (x)->Release(); (x) = NULL; } +#define SAFE_DELETE(x) { delete (x); (x) = NULL; } + +// From http://developer.amd.com/gpu_assets/Advanced%20DX9%20Capabilities%20for%20ATI%20Radeon%20Cards.pdf +// Magic FourCC's to unlock undocumented D3D9 features: + +// Z texture formats +#define FOURCC_INTZ ((D3DFORMAT)(MAKEFOURCC('I','N','T','Z'))) +#define FOURCC_RAWZ ((D3DFORMAT)(MAKEFOURCC('R','A','W','Z'))) +#define FOURCC_DF24 ((D3DFORMAT)(MAKEFOURCC('D','F','2','4'))) +#define FOURCC_DF16 ((D3DFORMAT)(MAKEFOURCC('D','F','1','6'))) + +// Depth buffer resolve: +#define FOURCC_RESZ ((D3DFORMAT)(MAKEFOURCC('R','E','S','Z'))) +#define RESZ_CODE 0x7fa05000 + +// Null render target to do Z-only shadow maps: (probably not useful for Dolphin) +#define FOURCC_NULL ((D3DFORMAT)(MAKEFOURCC('N','U','L','L'))) + +bool IsATIDevice(); +HRESULT Init(); +HRESULT Create(int adapter, HWND wnd, int resolution, int aa_mode, bool auto_depth); +void Close(); +void Shutdown(); + +// Direct access to the device. +extern LPDIRECT3DDEVICE9 dev; +extern bool bFrameInProgress; + +void Reset(); +bool BeginFrame(); +void EndFrame(); +void Present(); +bool CanUseINTZ(); + +int GetBackBufferWidth(); +int GetBackBufferHeight(); +LPDIRECT3DSURFACE9 GetBackBufferSurface(); +LPDIRECT3DSURFACE9 GetBackBufferDepthSurface(); +LPDIRECT3DVERTEXBUFFER9 GetquadVB(); +LPDIRECT3DVERTEXDECLARATION9 GetBasicvertexDecl(); +const D3DCAPS9 &GetCaps(); +const char *PixelShaderVersionString(); +const char *VertexShaderVersionString(); +void ShowD3DError(HRESULT err); + +// The following are "filtered" versions of the corresponding D3Ddev-> functions. +void SetTexture(DWORD Stage, IDirect3DBaseTexture9 *pTexture); +void SetRenderState(D3DRENDERSTATETYPE State, DWORD Value); +void RefreshRenderState(D3DRENDERSTATETYPE State); +void ChangeRenderState(D3DRENDERSTATETYPE State, DWORD Value); + +void SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value); +void RefreshTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type); +void ChangeTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value); + +void SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value); +void RefreshSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type); +void ChangeSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value); + +void RefreshVertexDeclaration(); +void SetVertexDeclaration(LPDIRECT3DVERTEXDECLARATION9 decl); + +void RefreshVertexShader(); +void SetVertexShader(LPDIRECT3DVERTEXSHADER9 shader); + +void RefreshPixelShader(); +void SetPixelShader(LPDIRECT3DPIXELSHADER9 shader); + +void ApplyCachedState(); + +// Utility functions for vendor specific hacks. So far, just the one. +void EnableAlphaToCoverage(); + +struct Resolution +{ + char name[32]; + int xres; + int yres; + std::set bitdepths; + std::set refreshes; +}; + +struct AALevel +{ + AALevel(const char *n, D3DMULTISAMPLE_TYPE m, int q) { + strncpy(name, n, 32); + name[31] = '\0'; + ms_setting = m; + qual_setting = q; + } + char name[32]; + D3DMULTISAMPLE_TYPE ms_setting; + int qual_setting; +}; + +struct Adapter +{ + D3DADAPTER_IDENTIFIER9 ident; + std::vector resolutions; + std::vector aa_levels; + bool supports_alpha_to_coverage; + + // Magic render targets, see the top of this file. + bool supports_intz; + bool supports_rawz; + bool supports_resz; + bool supports_null; +}; + +const Adapter &GetAdapter(int i); +const Adapter &GetCurAdapter(); +int GetNumAdapters(); + +} // namespace + + +// Used to not require the SDK and runtime versions to match: +// Linking with d3dx9.lib makes the most recent d3dx9_xx.dll of the +// compiler's SDK an actually unnecessary requirement. +// Add any d3dx9 functions which you want to use here and load them in LoadD3DX9() +typedef HRESULT (WINAPI* D3DXSAVESURFACETOFILEATYPE)(LPCSTR, D3DXIMAGE_FILEFORMAT, LPDIRECT3DSURFACE9, CONST PALETTEENTRY*, CONST RECT*); +typedef HRESULT (WINAPI* D3DXSAVETEXTURETOFILEATYPE)(LPCSTR, D3DXIMAGE_FILEFORMAT, LPDIRECT3DBASETEXTURE9, CONST PALETTEENTRY*); +typedef HRESULT (WINAPI* D3DXCOMPILESHADERTYPE)(LPCSTR, UINT, CONST D3DXMACRO*, LPD3DXINCLUDE, LPCSTR, LPCSTR, DWORD, LPD3DXBUFFER*, LPD3DXBUFFER*, LPD3DXCONSTANTTABLE*); + +extern D3DXSAVESURFACETOFILEATYPE PD3DXSaveSurfaceToFileA; +extern D3DXSAVETEXTURETOFILEATYPE PD3DXSaveTextureToFileA; +extern D3DXCOMPILESHADERTYPE PD3DXCompileShader; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DShader.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DShader.cpp new file mode 100644 index 0000000000..c7fd8764ee --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DShader.cpp @@ -0,0 +1,150 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "VideoConfig.h" +#include "DX9_D3DShader.h" + +namespace DX9 +{ + +namespace D3D +{ + +// Bytecode->shader. +LPDIRECT3DVERTEXSHADER9 CreateVertexShaderFromByteCode(const u8 *bytecode, int len) +{ + LPDIRECT3DVERTEXSHADER9 v_shader; + HRESULT hr = D3D::dev->CreateVertexShader((DWORD *)bytecode, &v_shader); + if (FAILED(hr)) + v_shader = 0; + return v_shader; +} + +// Code->bytecode. +bool CompileVertexShader(const char *code, int len, u8 **bytecode, int *bytecodelen) +{ + //try to compile + LPD3DXBUFFER shaderBuffer = 0; + LPD3DXBUFFER errorBuffer = 0; + HRESULT hr = PD3DXCompileShader(code, len, 0, 0, "main", D3D::VertexShaderVersionString(), + 0, &shaderBuffer, &errorBuffer, 0); + if (FAILED(hr)) + { + //compilation error + if (g_ActiveConfig.bShowShaderErrors) { + std::string hello = (char*)errorBuffer->GetBufferPointer(); + hello += "\n\n"; + hello += code; + MessageBoxA(0, hello.c_str(), "Error compiling vertex shader", MB_ICONERROR); + } + *bytecode = 0; + *bytecodelen = 0; + } + else if (SUCCEEDED(hr)) + { + *bytecodelen = shaderBuffer->GetBufferSize(); + *bytecode = new u8[*bytecodelen]; + memcpy(*bytecode, shaderBuffer->GetBufferPointer(), *bytecodelen); + } + + //cleanup + if (shaderBuffer) + shaderBuffer->Release(); + if (errorBuffer) + errorBuffer->Release(); + return SUCCEEDED(hr) ? true : false; +} + + +// Bytecode->shader. +LPDIRECT3DPIXELSHADER9 CreatePixelShaderFromByteCode(const u8 *bytecode, int len) +{ + LPDIRECT3DPIXELSHADER9 p_shader; + HRESULT hr = D3D::dev->CreatePixelShader((DWORD *)bytecode, &p_shader); + if (FAILED(hr)) + p_shader = 0; + return p_shader; +} + + +bool CompilePixelShader(const char *code, int len, u8 **bytecode, int *bytecodelen) +{ + LPD3DXBUFFER shaderBuffer = 0; + LPD3DXBUFFER errorBuffer = 0; + + // Someone: + // For some reason, I had this kind of errors : "Shader uses texture addressing operations + // in a dependency chain that is too complex for the target shader model (ps_2_0) to handle." + HRESULT hr = PD3DXCompileShader(code, len, 0, 0, "main", D3D::PixelShaderVersionString(), + 0, &shaderBuffer, &errorBuffer, 0); + + if (FAILED(hr)) + { + if (g_ActiveConfig.bShowShaderErrors) { + std::string hello = (char*)errorBuffer->GetBufferPointer(); + hello += "\n\n"; + hello += code; + MessageBoxA(0, hello.c_str(), "Error compiling pixel shader", MB_ICONERROR); + } + *bytecode = 0; + *bytecodelen = 0; + } + else if (SUCCEEDED(hr)) + { + *bytecodelen = shaderBuffer->GetBufferSize(); + *bytecode = new u8[*bytecodelen]; + memcpy(*bytecode, shaderBuffer->GetBufferPointer(), *bytecodelen); + } + + //cleanup + if (shaderBuffer) + shaderBuffer->Release(); + if (errorBuffer) + errorBuffer->Release(); + return SUCCEEDED(hr) ? true : false; +} + +LPDIRECT3DVERTEXSHADER9 CompileAndCreateVertexShader(const char *code, int len) { + u8 *bytecode; + int bytecodelen; + if (CompileVertexShader(code, len, &bytecode, &bytecodelen)) { + LPDIRECT3DVERTEXSHADER9 v_shader = CreateVertexShaderFromByteCode(bytecode, len); + delete [] bytecode; + return v_shader; + } else { + return 0; + } +} + +LPDIRECT3DPIXELSHADER9 CompileAndCreatePixelShader(const char *code, int len) { + u8 *bytecode; + int bytecodelen; + if (CompilePixelShader(code, len, &bytecode, &bytecodelen)) { + LPDIRECT3DPIXELSHADER9 p_shader = CreatePixelShaderFromByteCode(bytecode, len); + delete [] bytecode; + return p_shader; + } else { + return 0; + } +} + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DShader.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DShader.h new file mode 100644 index 0000000000..262fd345ae --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DShader.h @@ -0,0 +1,39 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "DX9_D3DBase.h" + +namespace DX9 +{ + +namespace D3D +{ + LPDIRECT3DVERTEXSHADER9 CreateVertexShaderFromByteCode(const u8 *bytecode, int len); + LPDIRECT3DPIXELSHADER9 CreatePixelShaderFromByteCode(const u8 *bytecode, int len); + + // The returned bytecode buffers should be delete[]-d. + bool CompileVertexShader(const char *code, int len, u8 **bytecode, int *bytecodelen); + bool CompilePixelShader(const char *code, int len, u8 **bytecode, int *bytecodelen); + + // Utility functions + LPDIRECT3DVERTEXSHADER9 CompileAndCreateVertexShader(const char *code, int len); + LPDIRECT3DPIXELSHADER9 CompileAndCreatePixelShader(const char *code, int len); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DTexture.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DTexture.cpp new file mode 100644 index 0000000000..ad427ac9b6 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DTexture.cpp @@ -0,0 +1,275 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "DX9_D3DBase.h" +#include "DX9_D3DTexture.h" + +namespace DX9 +{ + +namespace D3D +{ + +LPDIRECT3DTEXTURE9 CreateTexture2D(const u8* buffer, const int width, const int height, const int pitch, D3DFORMAT fmt, bool swap_r_b, int levels) +{ + u32* pBuffer = (u32*)buffer; + LPDIRECT3DTEXTURE9 pTexture; + + // crazy bitmagic, sorry :) + bool isPow2 = !((width&(width-1)) || (height&(height-1))); + bool bExpand = false; + + if (fmt == D3DFMT_A8P8) { + fmt = D3DFMT_A8L8; + bExpand = true; + } + + HRESULT hr; + // TODO(ector): Allow mipmaps for non-pow textures on newer cards? + // TODO(ector): Use the game-specified mipmaps? + if (levels > 0) + hr = dev->CreateTexture(width, height, levels, 0, fmt, D3DPOOL_MANAGED, &pTexture, NULL); + else + hr = dev->CreateTexture(width, height, 0, D3DUSAGE_AUTOGENMIPMAP, fmt, D3DPOOL_MANAGED, &pTexture, NULL); + + if (FAILED(hr)) + return 0; + int level = 0; + D3DLOCKED_RECT Lock; + pTexture->LockRect(level, &Lock, NULL, 0); + switch (fmt) + { + case D3DFMT_L8: + case D3DFMT_A8: + case D3DFMT_A4L4: + { + const u8 *pIn = buffer; + for (int y = 0; y < height; y++) + { + u8* pBits = ((u8*)Lock.pBits + (y * Lock.Pitch)); + memcpy(pBits, pIn, width); + pIn += pitch; + } + } + break; + case D3DFMT_R5G6B5: + { + const u16 *pIn = (u16*)buffer; + for (int y = 0; y < height; y++) + { + u16* pBits = (u16*)((u8*)Lock.pBits + (y * Lock.Pitch)); + memcpy(pBits, pIn, width * 2); + pIn += pitch; + } + } + break; + case D3DFMT_A8L8: + { + if (bExpand) { // I8 + const u8 *pIn = buffer; + // TODO(XK): Find a better way that does not involve either unpacking + // or downsampling (i.e. A4L4) + for (int y = 0; y < height; y++) + { + u8* pBits = ((u8*)Lock.pBits + (y * Lock.Pitch)); + for(int i = 0; i < width * 2; i += 2) { + pBits[i] = pIn[i / 2]; + pBits[i + 1] = pIn[i / 2]; + } + pIn += pitch; + } + } else { // IA8 + const u16 *pIn = (u16*)buffer; + + for (int y = 0; y < height; y++) + { + u16* pBits = (u16*)((u8*)Lock.pBits + (y * Lock.Pitch)); + memcpy(pBits, pIn, width * 2); + pIn += pitch; + } + } + } + break; + case D3DFMT_A8R8G8B8: + { + if(pitch * 4 == Lock.Pitch && !swap_r_b) + { + memcpy(Lock.pBits,buffer,Lock.Pitch*height); + } + else + { + u32* pIn = pBuffer; + if (!swap_r_b) { + for (int y = 0; y < height; y++) + { + u32 *pBits = (u32*)((u8*)Lock.pBits + (y * Lock.Pitch)); + memcpy(pBits, pIn, width * 4); + pIn += pitch; + } + } else { + for (int y = 0; y < height; y++) + { + u8 *pIn8 = (u8 *)pIn; + u8 *pBits = (u8 *)((u8*)Lock.pBits + (y * Lock.Pitch)); + for (int x = 0; x < width * 4; x += 4) { + pBits[x + 0] = pIn8[x + 2]; + pBits[x + 1] = pIn8[x + 1]; + pBits[x + 2] = pIn8[x + 0]; + pBits[x + 3] = pIn8[x + 3]; + } + pIn += pitch; + } + } + } + } + break; + case D3DFMT_DXT1: + memcpy(Lock.pBits,buffer,((width+3)/4)*((height+3)/4)*8); + break; + default: + PanicAlert("D3D: Invalid texture format %i", fmt); + } + pTexture->UnlockRect(level); + return pTexture; +} + +LPDIRECT3DTEXTURE9 CreateOnlyTexture2D(const int width, const int height, D3DFORMAT fmt) +{ + LPDIRECT3DTEXTURE9 pTexture; + // crazy bitmagic, sorry :) + bool isPow2 = !((width&(width-1)) || (height&(height-1))); + bool bExpand = false; + HRESULT hr; + // TODO(ector): Allow mipmaps for non-pow textures on newer cards? + // TODO(ector): Use the game-specified mipmaps? + if (!isPow2) + hr = dev->CreateTexture(width, height, 1, 0, fmt, D3DPOOL_MANAGED, &pTexture, NULL); + else + hr = dev->CreateTexture(width, height, 0, D3DUSAGE_AUTOGENMIPMAP, fmt, D3DPOOL_MANAGED, &pTexture, NULL); + + if (FAILED(hr)) + return 0; + return pTexture; +} + +void ReplaceTexture2D(LPDIRECT3DTEXTURE9 pTexture, const u8* buffer, const int width, const int height, const int pitch, D3DFORMAT fmt, bool swap_r_b, int level) +{ + u32* pBuffer = (u32*)buffer; + D3DLOCKED_RECT Lock; + pTexture->LockRect(level, &Lock, NULL, 0); + u32* pIn = pBuffer; + + bool bExpand = false; + + if (fmt == D3DFMT_A8P8) { + fmt = D3DFMT_A8L8; + bExpand = true; + } + switch (fmt) + { + case D3DFMT_A8R8G8B8: + if(pitch * 4 == Lock.Pitch && !swap_r_b) + { + memcpy(Lock.pBits, pBuffer, Lock.Pitch*height); + } + else if (!swap_r_b) + { + for (int y = 0; y < height; y++) + { + u32 *pBits = (u32*)((u8*)Lock.pBits + (y * Lock.Pitch)); + memcpy(pBits, pIn, width * 4); + pIn += pitch; + } + } + else + { + for (int y = 0; y < height; y++) + { + u8 *pIn8 = (u8 *)pIn; + u8 *pBits = (u8 *)((u8*)Lock.pBits + (y * Lock.Pitch)); + for (int x = 0; x < width * 4; x += 4) + { + pBits[x + 0] = pIn8[x + 2]; + pBits[x + 1] = pIn8[x + 1]; + pBits[x + 2] = pIn8[x + 0]; + pBits[x + 3] = pIn8[x + 3]; + } + pIn += pitch; + } + } + break; + case D3DFMT_L8: + case D3DFMT_A8: + case D3DFMT_A4L4: + { + const u8 *pIn = buffer; + for (int y = 0; y < height; y++) + { + u8* pBits = ((u8*)Lock.pBits + (y * Lock.Pitch)); + memcpy(pBits, pIn, width); + pIn += pitch; + } + } + break; + case D3DFMT_R5G6B5: + { + const u16 *pIn = (u16*)buffer; + for (int y = 0; y < height; y++) + { + u16* pBits = (u16*)((u8*)Lock.pBits + (y * Lock.Pitch)); + memcpy(pBits, pIn, width * 2); + pIn += pitch; + } + } + break; + case D3DFMT_A8L8: + { + if (bExpand) { // I8 + const u8 *pIn = buffer; + // TODO(XK): Find a better way that does not involve either unpacking + // or downsampling (i.e. A4L4) + for (int y = 0; y < height; y++) + { + u8* pBits = ((u8*)Lock.pBits + (y * Lock.Pitch)); + for(int i = 0; i < width * 2; i += 2) { + pBits[i] = pIn[i / 2]; + pBits[i + 1] = pIn[i / 2]; + } + pIn += pitch; + } + } else { // IA8 + const u16 *pIn = (u16*)buffer; + + for (int y = 0; y < height; y++) + { + u16* pBits = (u16*)((u8*)Lock.pBits + (y * Lock.Pitch)); + memcpy(pBits, pIn, width * 2); + pIn += pitch; + } + } + } + break; + case D3DFMT_DXT1: + memcpy(Lock.pBits,buffer,((width+3)/4)*((height+3)/4)*8); + break; + } + pTexture->UnlockRect(level); +} + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DTexture.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DTexture.h new file mode 100644 index 0000000000..46652a4964 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DTexture.h @@ -0,0 +1,34 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "DX9_D3DBase.h" + +namespace DX9 +{ + +namespace D3D +{ + LPDIRECT3DTEXTURE9 CreateTexture2D(const u8* buffer, const int width, const int height, const int pitch, D3DFORMAT fmt = D3DFMT_A8R8G8B8, bool swap_r_b = false, int levels = 1); + void ReplaceTexture2D(LPDIRECT3DTEXTURE9 pTexture, const u8* buffer, const int width, const int height, const int pitch, D3DFORMAT fmt, bool swap_r_b, int level = 0); + LPDIRECT3DTEXTURE9 CreateRenderTarget(const int width, const int height); + LPDIRECT3DSURFACE9 CreateDepthStencilSurface(const int width, const int height); + LPDIRECT3DTEXTURE9 CreateOnlyTexture2D(const int width, const int height, D3DFORMAT fmt); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DUtil.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DUtil.cpp new file mode 100644 index 0000000000..9974fce776 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DUtil.cpp @@ -0,0 +1,447 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "StringUtil.h" + +#include "DX9_D3DBase.h" +#include "DX9_D3DUtil.h" +#include "DX9_Render.h" + +namespace DX9 +{ + +namespace D3D +{ + +CD3DFont font; + +#define MAX_NUM_VERTICES 50*6 +struct FONT2DVERTEX { + float x,y,z; + float rhw; + u32 color; + float tu, tv; +}; + +#define D3DFVF_FONT2DVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1) +#define D3DFVF_FONT3DVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_NORMAL|D3DFVF_TEX1) + +inline FONT2DVERTEX InitFont2DVertex(float x, float y, u32 color, float tu, float tv) +{ + FONT2DVERTEX v; v.x=x; v.y=y; v.z=0; v.rhw=1.0f; v.color = color; v.tu = tu; v.tv = tv; + return v; +} + +CD3DFont::CD3DFont() +{ + m_pTexture = NULL; + m_pVB = NULL; +} + +enum {m_dwTexWidth = 512, m_dwTexHeight = 512}; + +int CD3DFont::Init() +{ + // Create vertex buffer for the letters + HRESULT hr; + if (FAILED(hr = dev->CreateVertexBuffer(MAX_NUM_VERTICES*sizeof(FONT2DVERTEX), + D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0, D3DPOOL_DEFAULT, &m_pVB, NULL))) + { + return hr; + } + m_fTextScale = 1.0f; // Draw fonts into texture without scaling + + // Prepare to create a bitmap + int *pBitmapBits; + BITMAPINFO bmi; + ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = (int)m_dwTexWidth; + bmi.bmiHeader.biHeight = -(int)m_dwTexHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biBitCount = 32; + + // Create a DC and a bitmap for the font + HDC hDC = CreateCompatibleDC(NULL); + HBITMAP hbmBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (VOID**)&pBitmapBits, NULL, 0); + SetMapMode(hDC, MM_TEXT); + + // Create a font. By specifying ANTIALIASED_QUALITY, we might get an + // antialiased font, but this is not guaranteed. + // We definitely don't want to get it cleartype'd, anyway. + int m_dwFontHeight = 24; + int nHeight = -MulDiv(m_dwFontHeight, int(GetDeviceCaps(hDC, LOGPIXELSY) * m_fTextScale), 72); + int dwBold = FW_NORMAL; ///FW_BOLD + HFONT hFont = CreateFont(nHeight, 0, 0, 0, dwBold, 0, + FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, + VARIABLE_PITCH, _T("Tahoma")); + if (NULL == hFont) + return E_FAIL; + + HGDIOBJ hOldbmBitmap = SelectObject(hDC, hbmBitmap); + HGDIOBJ hOldFont = SelectObject(hDC, hFont); + + // Set text properties + SetTextColor(hDC, 0xFFFFFF); + SetBkColor (hDC, 0); + SetTextAlign(hDC, TA_TOP); + + // Loop through all printable character and output them to the bitmap.. + // Meanwhile, keep track of the corresponding tex coords for each character. + int x = 0, y = 0; + char str[2] = "\0"; + for (int c = 0; c < 127 - 32; c++) + { + str[0] = c + 32; + SIZE size; + GetTextExtentPoint32A(hDC, str, 1, &size); + if ((int)(x+size.cx+1) > m_dwTexWidth) + { + x = 0; + y += size.cy + 1; + } + + ExtTextOutA(hDC, x+1, y+0, ETO_OPAQUE | ETO_CLIPPED, NULL, str, 1, NULL); + m_fTexCoords[c][0] = ((float)(x+0))/m_dwTexWidth; + m_fTexCoords[c][1] = ((float)(y+0))/m_dwTexHeight; + m_fTexCoords[c][2] = ((float)(x+0+size.cx))/m_dwTexWidth; + m_fTexCoords[c][3] = ((float)(y+0+size.cy))/m_dwTexHeight; + + x += size.cx + 3; //3 to work around annoying ij conflict (part of the j ends up with the i) + } + + // Create a new texture for the font + hr = dev->CreateTexture(m_dwTexWidth, m_dwTexHeight, 1, D3DUSAGE_DYNAMIC, + D3DFMT_A4R4G4B4, D3DPOOL_DEFAULT, &m_pTexture, NULL); + if (FAILED(hr)) + { + PanicAlert("Failed to create font texture"); + return hr; + } + + // Lock the surface and write the alpha values for the set pixels + D3DLOCKED_RECT d3dlr; + m_pTexture->LockRect(0, &d3dlr, 0, D3DLOCK_DISCARD); + int bAlpha; // 4-bit measure of pixel intensity + + for (y = 0; y < m_dwTexHeight; y++) + { + u16 *pDst16 = (u16*)((u8 *)d3dlr.pBits + y * d3dlr.Pitch); + for (x = 0; x < m_dwTexWidth; x++) + { + bAlpha = ((pBitmapBits[m_dwTexWidth * y + x] & 0xff) >> 4); + pDst16[x] = (bAlpha << 12) | 0x0fff; + } + } + + // Done updating texture, so clean up used objects + m_pTexture->UnlockRect(0); + + SelectObject(hDC, hOldbmBitmap); + DeleteObject(hbmBitmap); + + SelectObject(hDC, hOldFont); + DeleteObject(hFont); + + return S_OK; +} + +int CD3DFont::Shutdown() +{ + m_pVB->Release(); + m_pVB = NULL; + m_pTexture->Release(); + m_pTexture = NULL; + return S_OK; +} + + +const int RS[6][2] = +{ + {D3DRS_ALPHABLENDENABLE, TRUE}, + {D3DRS_SRCBLEND, D3DBLEND_SRCALPHA}, + {D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA}, + {D3DRS_CULLMODE, D3DCULL_NONE}, + {D3DRS_ZENABLE, FALSE}, + {D3DRS_FOGENABLE, FALSE}, +}; +const int TS[6][2] = +{ + {D3DTSS_COLOROP, D3DTOP_MODULATE}, + {D3DTSS_COLORARG1, D3DTA_TEXTURE}, + {D3DTSS_COLORARG2, D3DTA_DIFFUSE }, + {D3DTSS_ALPHAOP, D3DTOP_MODULATE }, + {D3DTSS_ALPHAARG1, D3DTA_TEXTURE }, + {D3DTSS_ALPHAARG2, D3DTA_DIFFUSE }, +}; + +static LPDIRECT3DPIXELSHADER9 ps_old = NULL; +static LPDIRECT3DVERTEXSHADER9 vs_old = NULL; + +void RestoreShaders() +{ + D3D::SetTexture(0, 0); + D3D::RefreshVertexDeclaration(); + D3D::RefreshPixelShader(); + D3D::RefreshVertexShader(); +} + +void RestoreRenderStates() +{ + RestoreShaders(); + for (int i = 0; i < 6; i++) + { + D3D::RefreshRenderState((_D3DRENDERSTATETYPE)RS[i][0]); + D3D::RefreshTextureStageState(0, (_D3DTEXTURESTAGESTATETYPE)int(TS[i][0])); + } +} + +void CD3DFont::SetRenderStates() +{ + D3D::SetTexture(0, m_pTexture); + + dev->SetPixelShader(0); + dev->SetVertexShader(0); + + dev->SetFVF(D3DFVF_FONT2DVERTEX); + + for (int i = 0; i < 6; i++) + { + D3D::ChangeRenderState((_D3DRENDERSTATETYPE)RS[i][0], RS[i][1]); + D3D::ChangeTextureStageState(0, (_D3DTEXTURESTAGESTATETYPE)int(TS[i][0]), TS[i][1]); + } +} + + +int CD3DFont::DrawTextScaled(float x, float y, float fXScale, float fYScale, float spacing, u32 dwColor, const char* strText, bool center) +{ + if (!m_pVB) + return 0; + + SetRenderStates(); + dev->SetStreamSource(0, m_pVB, 0, sizeof(FONT2DVERTEX)); + + float vpWidth = 1; + float vpHeight = 1; + + float sx = x*vpWidth-0.5f; + float sy = y*vpHeight-0.5f; + + float fStartX = sx; + + float invLineHeight = 1.0f / ((m_fTexCoords[0][3] - m_fTexCoords[0][1]) * m_dwTexHeight); + // Fill vertex buffer + FONT2DVERTEX* pVertices; + int dwNumTriangles = 0L; + m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD); + + const char *oldstrText=strText; + //First, let's measure the text + float tw=0; + float mx=0; + float maxx=0; + + while (*strText) + { + char c = *strText++; + + if (c == ('\n')) + mx = 0; + if (c < (' ')) + continue; + + float tx1 = m_fTexCoords[c-32][0]; + float tx2 = m_fTexCoords[c-32][2]; + + float w = (tx2-tx1)*m_dwTexWidth; + w *= (fXScale*vpHeight)*invLineHeight; + mx += w + spacing*fXScale*vpWidth; + if (mx > maxx) maxx = mx; + } + + float offset = -maxx/2; + strText = oldstrText; + //Then let's draw it + if (center) + { + sx+=offset; + fStartX+=offset; + } + + float wScale = (fXScale*vpHeight)*invLineHeight; + float hScale = (fYScale*vpHeight)*invLineHeight; + + while (*strText) + { + char c = *strText++; + + if (c == ('\n')) + { + sx = fStartX; + sy += fYScale*vpHeight; + } + if (c < (' ')) + continue; + + c-=32; + float tx1 = m_fTexCoords[c][0]; + float ty1 = m_fTexCoords[c][1]; + float tx2 = m_fTexCoords[c][2]; + float ty2 = m_fTexCoords[c][3]; + + float w = (tx2-tx1)*m_dwTexWidth; + float h = (ty2-ty1)*m_dwTexHeight; + + w *= wScale; + h *= hScale; + + FONT2DVERTEX v[6]; + v[0] = InitFont2DVertex(sx, sy+h, dwColor, tx1, ty2); + v[1] = InitFont2DVertex(sx, sy, dwColor, tx1, ty1); + v[2] = InitFont2DVertex(sx+w, sy+h, dwColor, tx2, ty2); + v[3] = InitFont2DVertex(sx+w, sy, dwColor, tx2, ty1); + v[4] = v[2]; + v[5] = v[1]; + + memcpy(pVertices, v, 6*sizeof(FONT2DVERTEX)); + + pVertices+=6; + dwNumTriangles += 2; + + if (dwNumTriangles * 3 > (MAX_NUM_VERTICES - 6)) + { + // Unlock, render, and relock the vertex buffer + m_pVB->Unlock(); + dev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles); + m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD); + dwNumTriangles = 0; + } + + sx += w + spacing*fXScale*vpWidth; + } + + // Unlock and render the vertex buffer + m_pVB->Unlock(); + if (dwNumTriangles > 0) + dev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles); + RestoreRenderStates(); + return S_OK; +} + +void quad2d(float x1, float y1, float x2, float y2, u32 color, float u1, float v1, float u2, float v2) +{ + struct Q2DVertex { float x,y,z,rhw;u32 color;float u,v,w,h; } coords[4] = { + {x1-0.5f, y1-0.5f, 0, 1, color, u1, v1}, + {x2-0.5f, y1-0.5f, 0, 1, color, u2, v1}, + {x2-0.5f, y2-0.5f, 0, 1, color, u2, v2}, + {x1-0.5f, y2-0.5f, 0, 1, color, u1, v2}, + }; + dev->SetPixelShader(0); + dev->SetVertexShader(0); + dev->SetVertexDeclaration(NULL); + dev->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1); + dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, coords, sizeof(Q2DVertex)); + RestoreShaders(); +} + +void drawShadedTexQuad(IDirect3DTexture9 *texture, + const RECT *rSource, + int SourceWidth, + int SourceHeight, + int DestWidth, + int DestHeight, + IDirect3DPixelShader9 *PShader, + IDirect3DVertexShader9 *Vshader) +{ + float sw = 1.0f /(float) SourceWidth; + float sh = 1.0f /(float) SourceHeight; + float dw = 1.0f /(float) DestWidth; + float dh = 1.0f /(float) DestHeight; + float u1=((float)rSource->left) * sw; + float u2=((float)rSource->right) * sw; + float v1=((float)rSource->top) * sh; + float v2=((float)rSource->bottom) * sh; + + struct Q2DVertex { float x,y,z,rhw,u,v,w,h,L,T,R,B; } coords[4] = { + {-1.0f - dw,-1.0f + dh, 0.0f,1.0f, u1, v2, sw, sh,u1,v1,u2,v2}, + {-1.0f - dw, 1.0f + dh, 0.0f,1.0f, u1, v1, sw, sh,u1,v1,u2,v2}, + { 1.0f - dw,-1.0f + dh, 0.0f,1.0f, u2, v2, sw, sh,u1,v1,u2,v2}, + { 1.0f - dw, 1.0f + dh, 0.0f,1.0f, u2, v1, sw, sh,u1,v1,u2,v2} + }; + dev->SetVertexShader(Vshader); + dev->SetPixelShader(PShader); + D3D::SetTexture(0, texture); + dev->SetFVF(D3DFVF_XYZW | D3DFVF_TEX3 | D3DFVF_TEXCOORDSIZE4(2)); + dev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, coords, sizeof(Q2DVertex)); + RestoreShaders(); +} + +void drawShadedTexSubQuad(IDirect3DTexture9 *texture, + const MathUtil::Rectangle *rSource, + int SourceWidth, + int SourceHeight, + const MathUtil::Rectangle *rDest, + int DestWidth, + int DestHeight, + IDirect3DPixelShader9 *PShader, + IDirect3DVertexShader9 *Vshader) +{ + float sw = 1.0f /(float) SourceWidth; + float sh = 1.0f /(float) SourceHeight; + float dw = 1.0f /(float) DestWidth; + float dh = 1.0f /(float) DestHeight; + float u1= rSource->left * sw; + float u2= rSource->right * sw; + float v1= rSource->top * sh; + float v2= rSource->bottom * sh; + + struct Q2DVertex { float x,y,z,rhw,u,v,w,h,L,T,R,B; } coords[4] = { + { rDest->left - dw , rDest->top + dh, 1.0f,1.0f, u1, v2, sw, sh,u1,v1,u2,v2}, + { rDest->left - dw , rDest->bottom + dh, 1.0f,1.0f, u1, v1, sw, sh,u1,v1,u2,v2}, + { rDest->right - dw , rDest->top + dh, 1.0f,1.0f, u2, v2, sw, sh,u1,v1,u2,v2}, + { rDest->right - dw , rDest->bottom + dh, 1.0f,1.0f, u2, v1, sw, sh,u1,v1,u2,v2} + }; + dev->SetVertexShader(Vshader); + dev->SetPixelShader(PShader); + D3D::SetTexture(0, texture); + dev->SetFVF(D3DFVF_XYZW | D3DFVF_TEX3 | D3DFVF_TEXCOORDSIZE4(2)); + dev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, coords, sizeof(Q2DVertex)); + RestoreShaders(); +} + +void drawClearQuad(u32 Color,float z,IDirect3DPixelShader9 *PShader,IDirect3DVertexShader9 *Vshader) +{ + struct Q2DVertex { float x,y,z,rhw;u32 color;} coords[4] = { + {-1.0f, 1.0f, z, 1.0f, Color}, + { 1.0f, 1.0f, z, 1.0f, Color}, + { 1.0f, -1.0f, z, 1.0f, Color}, + {-1.0f, -1.0f, z, 1.0f, Color} + }; + dev->SetVertexShader(Vshader); + dev->SetPixelShader(PShader); + dev->SetFVF(D3DFVF_XYZW | D3DFVF_DIFFUSE); + dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, coords, sizeof(Q2DVertex)); + RestoreShaders(); +} + + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DUtil.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DUtil.h new file mode 100644 index 0000000000..4beeb1ea27 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_D3DUtil.h @@ -0,0 +1,88 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "DX9_D3DBase.h" +#include +#include + +namespace DX9 +{ + +namespace D3D +{ + // Font creation flags + #define D3DFONT_BOLD 0x0001 + #define D3DFONT_ITALIC 0x0002 + + // Font rendering flags + #define D3DFONT_CENTERED 0x0001 + + //a cut-down variant of the DXSDK CD3DFont class + class CD3DFont + { + LPDIRECT3DTEXTURE9 m_pTexture; // The d3d texture for this font + LPDIRECT3DVERTEXBUFFER9 m_pVB; // VertexBuffer for rendering text + //int m_dwTexWidth; // Texture dimensions + //int m_dwTexHeight; + float m_fTextScale; + float m_fTexCoords[128-32][4]; + + public: + CD3DFont(); + // 2D (no longer 3D) text drawing function + // Initializing and destroying device-dependent objects + void SetRenderStates(); + int Init(); + int Shutdown(); + int DrawTextScaled( float x, float y, + float fXScale, float fYScale, + float spacing, u32 dwColor, + const char* strText, bool center=true ); + + + // Constructor / destructor + //~CD3DFont(); + }; + + extern CD3DFont font; + + void quad2d(float x1, float y1, float x2, float y2, u32 color, float u1=0, float v1=0, float u2=1, float v2=1); + void drawShadedTexQuad(IDirect3DTexture9 *texture, + const RECT *rSource, + int SourceWidth, + int SourceHeight, + int DestWidth, + int DestHeight, + IDirect3DPixelShader9 *PShader, + IDirect3DVertexShader9 *Vshader); + void drawShadedTexSubQuad(IDirect3DTexture9 *texture, + const MathUtil::Rectangle *rSource, + int SourceWidth, + int SourceHeight, + const MathUtil::Rectangle *rDest, + int DestWidth, + int DestHeight, + IDirect3DPixelShader9 *PShader, + IDirect3DVertexShader9 *Vshader); + void drawClearQuad(u32 Color,float z,IDirect3DPixelShader9 *PShader,IDirect3DVertexShader9 *Vshader); + void SaveRenderStates(); + void RestoreRenderStates(); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_FramebufferManager.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_FramebufferManager.cpp new file mode 100644 index 0000000000..485be8c4d3 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_FramebufferManager.cpp @@ -0,0 +1,285 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "DX9_D3DBase.h" +#include "DX9_Render.h" +#include "DX9_FramebufferManager.h" +#include "VideoConfig.h" +#include "DX9_PixelShaderCache.h" +#include "DX9_VertexShaderCache.h" +#include "DX9_TextureConverter.h" + +#include "../Main.h" + +#undef CHECK +#define CHECK(hr, Message, ...) if (FAILED(hr)) { PanicAlert(__FUNCTION__ "Failed in %s at line %d: " Message, __FILE__, __LINE__, __VA_ARGS__); } + +namespace DX9 +{ + +XFBSource FramebufferManager::m_realXFBSource; + +LPDIRECT3DTEXTURE9 FramebufferManager::s_efb_color_texture; +LPDIRECT3DTEXTURE9 FramebufferManager::s_efb_colorRead_texture; +LPDIRECT3DTEXTURE9 FramebufferManager::s_efb_depth_texture; +LPDIRECT3DTEXTURE9 FramebufferManager::s_efb_depthRead_texture; + +LPDIRECT3DSURFACE9 FramebufferManager::s_efb_depth_surface; +LPDIRECT3DSURFACE9 FramebufferManager::s_efb_color_surface; +LPDIRECT3DSURFACE9 FramebufferManager::s_efb_color_ReadBuffer; +LPDIRECT3DSURFACE9 FramebufferManager::s_efb_depth_ReadBuffer; +LPDIRECT3DSURFACE9 FramebufferManager::s_efb_color_OffScreenReadBuffer; +LPDIRECT3DSURFACE9 FramebufferManager::s_efb_depth_OffScreenReadBuffer; + +D3DFORMAT FramebufferManager::s_efb_color_surface_Format; +D3DFORMAT FramebufferManager::s_efb_depth_surface_Format; +D3DFORMAT FramebufferManager::s_efb_depth_ReadBuffer_Format; + +LPDIRECT3DSURFACE9 FramebufferManager::GetEFBColorRTSurface() +{ + return s_efb_color_surface; +} + +LPDIRECT3DSURFACE9 FramebufferManager::GetEFBDepthRTSurface() +{ + return s_efb_depth_surface; +} + +LPDIRECT3DSURFACE9 FramebufferManager::GetEFBColorOffScreenRTSurface() +{ + return s_efb_color_OffScreenReadBuffer; +} + +LPDIRECT3DSURFACE9 FramebufferManager::GetEFBDepthOffScreenRTSurface() +{ + return s_efb_depth_OffScreenReadBuffer; +} + +LPDIRECT3DSURFACE9 FramebufferManager::GetEFBColorReadSurface() +{ + return s_efb_color_ReadBuffer; +} + +LPDIRECT3DSURFACE9 FramebufferManager::GetEFBDepthReadSurface() +{ + return s_efb_depth_ReadBuffer; +} + +D3DFORMAT FramebufferManager::GetEFBDepthRTSurfaceFormat() +{ + return s_efb_depth_surface_Format; +} + +D3DFORMAT FramebufferManager::GetEFBDepthReadSurfaceFormat() +{ + return s_efb_depth_ReadBuffer_Format; +} + +D3DFORMAT FramebufferManager::GetEFBColorRTSurfaceFormat() +{ + return s_efb_color_surface_Format; +} + +LPDIRECT3DTEXTURE9 FramebufferManager::GetEFBColorTexture(const EFBRectangle& sourceRc) +{ + return s_efb_color_texture; +} + +LPDIRECT3DTEXTURE9 FramebufferManager::GetEFBDepthTexture(const EFBRectangle &sourceRc) +{ + return s_efb_depth_texture; +} + +FramebufferManager::FramebufferManager() +{ + s_efb_color_texture = NULL; + LPDIRECT3DTEXTURE9 s_efb_colorRead_texture = NULL; + LPDIRECT3DTEXTURE9 s_efb_depth_texture = NULL; + LPDIRECT3DTEXTURE9 s_efb_depthRead_texture = NULL; + + LPDIRECT3DSURFACE9 s_efb_depth_surface = NULL; + LPDIRECT3DSURFACE9 s_efb_color_surface = NULL; + LPDIRECT3DSURFACE9 s_efb_color_ReadBuffer = NULL; + LPDIRECT3DSURFACE9 s_efb_depth_ReadBuffer = NULL; + LPDIRECT3DSURFACE9 s_efb_color_OffScreenReadBuffer = NULL; + LPDIRECT3DSURFACE9 s_efb_depth_OffScreenReadBuffer = NULL; + + D3DFORMAT s_efb_color_surface_Format = D3DFMT_FORCE_DWORD; + D3DFORMAT s_efb_depth_surface_Format = D3DFMT_FORCE_DWORD; + D3DFORMAT s_efb_depth_ReadBuffer_Format = D3DFMT_FORCE_DWORD; + m_realXFBSource.texture = NULL; + + // Simplest possible setup to start with. + int target_width = Renderer::GetFullTargetWidth(); + int target_height = Renderer::GetFullTargetHeight(); + + s_efb_color_surface_Format = D3DFMT_A8R8G8B8; + // Get the framebuffer texture + HRESULT hr = D3D::dev->CreateTexture(target_width, target_height, 1, D3DUSAGE_RENDERTARGET, s_efb_color_surface_Format, + D3DPOOL_DEFAULT, &s_efb_color_texture, NULL); + if(s_efb_color_texture) + { + hr = s_efb_color_texture->GetSurfaceLevel(0, &s_efb_color_surface); + } + CHECK(hr, "Create color texture (size: %dx%d; hr=%#x)", target_width, target_height, hr); + hr = D3D::dev->CreateTexture(1, 1, 1, D3DUSAGE_RENDERTARGET, s_efb_color_surface_Format, + D3DPOOL_DEFAULT, &s_efb_colorRead_texture, NULL); + CHECK(hr, "Create Color Read Texture (hr=%#x)", hr); + if(s_efb_colorRead_texture) + { + s_efb_colorRead_texture->GetSurfaceLevel(0, &s_efb_color_ReadBuffer); + } + // Create an offscreen surface that we can lock to retrieve the data + hr = D3D::dev->CreateOffscreenPlainSurface(1, 1, s_efb_color_surface_Format, D3DPOOL_SYSTEMMEM, &s_efb_color_OffScreenReadBuffer, NULL); + CHECK(hr, "Create offscreen color surface (hr=%#x)", hr); + + // Select a Z-buffer format with hardware support + D3DFORMAT *DepthTexFormats = new D3DFORMAT[5]; + DepthTexFormats[0] = FOURCC_INTZ; + DepthTexFormats[1] = FOURCC_DF24; + DepthTexFormats[2] = FOURCC_RAWZ; + DepthTexFormats[3] = FOURCC_DF16; + DepthTexFormats[4] = D3DFMT_D24X8; + + for(int i = 0; i < 5; i++) + { + s_efb_depth_surface_Format = DepthTexFormats[i]; + // Create the framebuffer depth texture + hr = D3D::dev->CreateTexture(target_width, target_height, 1, D3DUSAGE_DEPTHSTENCIL, s_efb_depth_surface_Format, + D3DPOOL_DEFAULT, &s_efb_depth_texture, NULL); + if (!FAILED(hr)) + break; + } + CHECK(hr, "Framebuffer depth texture (size: %dx%d; hr=%#x)", target_width, target_height, hr); + // Get the Surface + if(s_efb_depth_texture) + { + s_efb_depth_texture->GetSurfaceLevel(0, &s_efb_depth_surface); + } + // Create a 4x4 pixel texture to work as a buffer for peeking + if(s_efb_depth_surface_Format == FOURCC_RAWZ || s_efb_depth_surface_Format == D3DFMT_D24X8) + { + DepthTexFormats[0] = D3DFMT_A8R8G8B8; + } + else + { + DepthTexFormats[0] = D3DFMT_R32F; + } + DepthTexFormats[1] = D3DFMT_A8R8G8B8; + + for(int i = 0; i < 2; i++) + { + s_efb_depth_ReadBuffer_Format = DepthTexFormats[i]; + // Get the framebuffer Depth texture + hr = D3D::dev->CreateTexture(4, 4, 1, D3DUSAGE_RENDERTARGET, s_efb_depth_ReadBuffer_Format, + D3DPOOL_DEFAULT, &s_efb_depthRead_texture, NULL); + if (!FAILED(hr)) + break; + } + + CHECK(hr, "Create depth read texture (hr=%#x)", hr); + if(s_efb_depthRead_texture) + { + s_efb_depthRead_texture->GetSurfaceLevel(0, &s_efb_depth_ReadBuffer); + } + // Create an offscreen surface that we can lock to retrieve the data + hr = D3D::dev->CreateOffscreenPlainSurface(4, 4, s_efb_depth_ReadBuffer_Format, D3DPOOL_SYSTEMMEM, &s_efb_depth_OffScreenReadBuffer, NULL); + CHECK(hr, "Create depth offscreen surface (hr=%#x)", hr); + delete [] DepthTexFormats; +} + +FramebufferManager::~FramebufferManager() +{ + SAFE_RELEASE(s_efb_depth_surface); + SAFE_RELEASE(s_efb_color_surface); + SAFE_RELEASE(s_efb_color_ReadBuffer); + SAFE_RELEASE(s_efb_depth_ReadBuffer); + SAFE_RELEASE(s_efb_color_OffScreenReadBuffer); + SAFE_RELEASE(s_efb_depth_OffScreenReadBuffer); + SAFE_RELEASE(s_efb_color_texture); + SAFE_RELEASE(s_efb_colorRead_texture); + SAFE_RELEASE(s_efb_depth_texture); + SAFE_RELEASE(s_efb_depthRead_texture); + + if (m_realXFBSource.texture) + m_realXFBSource.texture->Release(); + m_realXFBSource.texture = NULL; +} + +void FramebufferManager::copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + // TODO: + + //u8* xfb_in_ram = Memory_GetPtr(xfbAddr); + //if (!xfb_in_ram) + //{ + // WARN_LOG(VIDEO, "Tried to copy to invalid XFB address"); + // return; + //} + + //TargetRectangle targetRc = Renderer::ConvertEFBRectangle(sourceRc); + //TextureConverter::EncodeToRamYUYV(GetEFBColorTexture(sourceRc), targetRc, xfb_in_ram, fbWidth, fbHeight); +} + +const XFBSource** FramebufferManager::getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount) +{ + return NULL; + + // TODO: + //xfbCount = 1; + + //m_realXFBSource.texWidth = fbWidth; + //m_realXFBSource.texHeight = fbHeight; + + //m_realXFBSource.srcAddr = xfbAddr; + //m_realXFBSource.srcWidth = fbWidth; + //m_realXFBSource.srcHeight = fbHeight; + + //if (!m_realXFBSource.texture) + //{ + // D3D::dev->CreateTexture(fbWidth, fbHeight, 1, D3DUSAGE_RENDERTARGET, s_efb_color_surface_Format, + // D3DPOOL_DEFAULT, &m_realXFBSource.texture, NULL); + //} + + //// Decode YUYV data from GameCube RAM + //TextureConverter::DecodeToTexture(xfbAddr, fbWidth, fbHeight, m_realXFBSource.texture); + + //m_overlappingXFBArray[0] = &m_realXFBSource; + + //return &m_overlappingXFBArray[0]; +} + +XFBSourceBase *FramebufferManager::CreateXFBSource(unsigned int target_width, unsigned int target_height) +{ + XFBSource* const xfbs = new XFBSource; + D3D::dev->CreateTexture(target_width, target_height, 1, D3DUSAGE_RENDERTARGET, s_efb_color_surface_Format, + D3DPOOL_DEFAULT, &xfbs->texture, NULL); + + return xfbs; +} + +XFBSource::~XFBSource() +{ + texture->Release(); +} + +void XFBSource::CopyEFB(const TargetRectangle& efbSource) +{ + +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_FramebufferManager.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_FramebufferManager.h new file mode 100644 index 0000000000..d15ed0b17c --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_FramebufferManager.h @@ -0,0 +1,130 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _FRAMEBUFFERMANAGER_D3D_H_ +#define _FRAMEBUFFERMANAGER_D3D_H_ + +#include +#include "DX9_D3DBase.h" + +#include "../FramebufferManager.h" + +// On the GameCube, the game sends a request for the graphics processor to +// transfer its internal EFB (Embedded Framebuffer) to an area in GameCube RAM +// called the XFB (External Framebuffer). The size and location of the XFB is +// decided at the time of the copy, and the format is always YUYV. The video +// interface is given a pointer to the XFB, which will be decoded and +// displayed on the TV. +// +// There are two ways for Dolphin to emulate this: +// +// Real XFB mode: +// +// Dolphin will behave like the GameCube and encode the EFB to +// a portion of GameCube RAM. The emulated video interface will decode the data +// for output to the screen. +// +// Advantages: Behaves exactly like the GameCube. +// Disadvantages: Resolution will be limited. +// +// Virtual XFB mode: +// +// When a request is made to copy the EFB to an XFB, Dolphin +// will remember the RAM location and size of the XFB in a Virtual XFB list. +// The video interface will look up the XFB in the list and use the enhanced +// data stored there, if available. +// +// Advantages: Enables high resolution graphics, better than real hardware. +// Disadvantages: If the GameCube CPU writes directly to the XFB (which is +// possible but uncommon), the Virtual XFB will not capture this information. + +// There may be multiple XFBs in GameCube RAM. This is the maximum number to +// virtualize. + +namespace DX9 +{ + +const int MAX_VIRTUAL_XFB = 8; + +inline bool addrRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper) +{ + return !((aLower >= bUpper) || (bLower >= aUpper)); +} + +struct XFBSource : public XFBSourceBase +{ + XFBSource() : texture(NULL) {} + ~XFBSource(); + + void CopyEFB(const TargetRectangle& efbSource); + + LPDIRECT3DTEXTURE9 texture; +}; + +class FramebufferManager : public ::FramebufferManagerBase +{ +public: + FramebufferManager(); + ~FramebufferManager(); + + void CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + + XFBSourceBase *CreateXFBSource(unsigned int target_width, unsigned int target_height); + + static LPDIRECT3DTEXTURE9 GetEFBColorTexture(const EFBRectangle& sourceRc); + static LPDIRECT3DTEXTURE9 GetEFBDepthTexture(const EFBRectangle& sourceRc); + + static LPDIRECT3DSURFACE9 GetEFBColorRTSurface(); + static LPDIRECT3DSURFACE9 GetEFBDepthRTSurface(); + static LPDIRECT3DSURFACE9 GetEFBColorOffScreenRTSurface(); + static LPDIRECT3DSURFACE9 GetEFBDepthOffScreenRTSurface(); + static D3DFORMAT GetEFBDepthRTSurfaceFormat(); + static D3DFORMAT GetEFBColorRTSurfaceFormat(); + static D3DFORMAT GetEFBDepthReadSurfaceFormat(); + static LPDIRECT3DSURFACE9 GetEFBColorReadSurface(); + static LPDIRECT3DSURFACE9 GetEFBDepthReadSurface(); + +private: + + void copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + void copyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + const XFBSource** getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount); + + static XFBSource m_realXFBSource; // Only used in Real XFB mode + + const XFBSource* m_overlappingXFBArray[MAX_VIRTUAL_XFB]; + + static LPDIRECT3DTEXTURE9 s_efb_color_texture;//Texture thats contains the color data of the render target + static LPDIRECT3DTEXTURE9 s_efb_colorRead_texture;//1 pixel texture for temporal data store + static LPDIRECT3DTEXTURE9 s_efb_depth_texture;//Texture thats contains the depth data of the render target + static LPDIRECT3DTEXTURE9 s_efb_depthRead_texture;//4 pixel texture for temporal data store + + static LPDIRECT3DSURFACE9 s_efb_depth_surface;//Depth Surface + static LPDIRECT3DSURFACE9 s_efb_color_surface;//Color Surface + static LPDIRECT3DSURFACE9 s_efb_color_ReadBuffer;//Surface 0 of s_efb_colorRead_texture + static LPDIRECT3DSURFACE9 s_efb_depth_ReadBuffer;//Surface 0 of s_efb_depthRead_texture + static LPDIRECT3DSURFACE9 s_efb_color_OffScreenReadBuffer;//System memory Surface that can be locked to retriebe the data + static LPDIRECT3DSURFACE9 s_efb_depth_OffScreenReadBuffer;//System memory Surface that can be locked to retriebe the data + + static D3DFORMAT s_efb_color_surface_Format;//Format of the color Surface + static D3DFORMAT s_efb_depth_surface_Format;//Format of the Depth Surface + static D3DFORMAT s_efb_depth_ReadBuffer_Format;//Format of the Depth color Read Surface +}; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_NativeVertexFormat.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_NativeVertexFormat.cpp new file mode 100644 index 0000000000..5bad3639bf --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_NativeVertexFormat.cpp @@ -0,0 +1,178 @@ + +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "DX9_D3DBase.h" + +#include "Profiler.h" +#include "x64Emitter.h" +#include "ABI.h" +#include "MemoryUtil.h" +#include "VertexShaderGen.h" + +#include "CPMemory.h" + +#include "DX9_VertexManager.h" + +namespace DX9 +{ + +class D3DVertexFormat : public NativeVertexFormat +{ + LPDIRECT3DVERTEXDECLARATION9 d3d_decl; + +public: + D3DVertexFormat(); + ~D3DVertexFormat(); + virtual void Initialize(const PortableVertexDeclaration &_vtx_decl); + virtual void SetupVertexPointers() const; +}; + +NativeVertexFormat *VertexManager::CreateNativeVertexFormat() +{ + return new D3DVertexFormat(); +} + +D3DVertexFormat::D3DVertexFormat() : d3d_decl(NULL) +{ +} + +D3DVertexFormat::~D3DVertexFormat() +{ + if (d3d_decl) + { + d3d_decl->Release(); + d3d_decl = NULL; + } +} + +D3DDECLTYPE VarToD3D(VarType t, int size) +{ + if (t < 0 || t > 4) { + PanicAlert("VarToD3D: Invalid VarType %i", t); + } + static const D3DDECLTYPE lookup1[5] = { + D3DDECLTYPE_UNUSED, D3DDECLTYPE_UNUSED, D3DDECLTYPE_UNUSED, D3DDECLTYPE_UNUSED, D3DDECLTYPE_FLOAT1, + }; + static const D3DDECLTYPE lookup2[5] = { + D3DDECLTYPE_UNUSED, D3DDECLTYPE_UNUSED, D3DDECLTYPE_SHORT2N, D3DDECLTYPE_USHORT2N, D3DDECLTYPE_FLOAT2, + }; + static const D3DDECLTYPE lookup3[5] = { + D3DDECLTYPE_UNUSED, D3DDECLTYPE_UNUSED, D3DDECLTYPE_UNUSED, D3DDECLTYPE_UNUSED, D3DDECLTYPE_FLOAT3, + }; + // Sadly, D3D9 has no SBYTE4N. D3D10 does, though. + static const D3DDECLTYPE lookup4[5] = { + D3DDECLTYPE_UNUSED, D3DDECLTYPE_UBYTE4N, D3DDECLTYPE_SHORT4N, D3DDECLTYPE_USHORT4N, D3DDECLTYPE_FLOAT4, + }; + D3DDECLTYPE retval = D3DDECLTYPE_UNUSED; + switch (size) { + case 1: retval = lookup1[t]; break; + case 2: retval = lookup2[t]; break; + case 3: retval = lookup3[t]; break; + case 4: retval = lookup4[t]; break; + default: PanicAlert("VarToD3D: size wrong (%i)", size); break; + } + if (retval == D3DDECLTYPE_UNUSED) { + PanicAlert("VarToD3D: Invalid type/size combo %i , %i", (int)t, size); + } + return retval; +} + +void D3DVertexFormat::Initialize(const PortableVertexDeclaration &_vtx_decl) +{ + vertex_stride = _vtx_decl.stride; + + D3DVERTEXELEMENT9 *elems = new D3DVERTEXELEMENT9[32]; + memset(elems, 0, sizeof(D3DVERTEXELEMENT9) * 32); + + // There's only one stream and it's 0, so the above memset takes care of that - no need to set Stream. + // Same for method. + + // So, here we go. First position: + int elem_idx = 0; + elems[elem_idx].Offset = 0; // Positions are always first, at position 0. Always float3. + elems[elem_idx].Type = D3DDECLTYPE_FLOAT3; + elems[elem_idx].Usage = D3DDECLUSAGE_POSITION; + ++elem_idx; + + for (int i = 0; i < 3; i++) + { + if (_vtx_decl.normal_offset[i] > 0) + { + elems[elem_idx].Offset = _vtx_decl.normal_offset[i]; + elems[elem_idx].Type = VarToD3D(_vtx_decl.normal_gl_type, _vtx_decl.normal_gl_size); + elems[elem_idx].Usage = D3DDECLUSAGE_NORMAL; + elems[elem_idx].UsageIndex = i; + ++elem_idx; + } + } + + for (int i = 0; i < 2; i++) + { + if (_vtx_decl.color_offset[i] > 0) + { + elems[elem_idx].Offset = _vtx_decl.color_offset[i]; + elems[elem_idx].Type = VarToD3D(_vtx_decl.color_gl_type, 4); + elems[elem_idx].Usage = D3DDECLUSAGE_COLOR; + elems[elem_idx].UsageIndex = i; + ++elem_idx; + } + } + + for (int i = 0; i < 8; i++) + { + if (_vtx_decl.texcoord_offset[i] > 0) + { + elems[elem_idx].Offset = _vtx_decl.texcoord_offset[i]; + elems[elem_idx].Type = VarToD3D(_vtx_decl.texcoord_gl_type[i], _vtx_decl.texcoord_size[i]); + elems[elem_idx].Usage = D3DDECLUSAGE_TEXCOORD; + elems[elem_idx].UsageIndex = i; + ++elem_idx; + } + } + + if (_vtx_decl.posmtx_offset != -1) + { + elems[elem_idx].Offset = _vtx_decl.posmtx_offset; + elems[elem_idx].Usage = D3DDECLUSAGE_BLENDINDICES; + elems[elem_idx].Type = D3DDECLTYPE_D3DCOLOR; + elems[elem_idx].UsageIndex = 0; + ++elem_idx; + } + + // End marker + elems[elem_idx].Stream = 0xff; + elems[elem_idx].Type = D3DDECLTYPE_UNUSED; + ++elem_idx; + + if (FAILED(D3D::dev->CreateVertexDeclaration(elems, &d3d_decl))) + { + PanicAlert("Failed to create D3D vertex declaration!"); + return; + } + delete [] elems; +} + +void D3DVertexFormat::SetupVertexPointers() const +{ + if (d3d_decl) + D3D::SetVertexDeclaration(d3d_decl); + else + ERROR_LOG(VIDEO, "invalid d3d decl"); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_PixelShaderCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_PixelShaderCache.cpp new file mode 100644 index 0000000000..90544d0114 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_PixelShaderCache.cpp @@ -0,0 +1,392 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "Common.h" +#include "Hash.h" +#include "FileUtil.h" +#include "LinearDiskCache.h" + +#include "DX9_D3DBase.h" +#include "DX9_D3DShader.h" +#include "Statistics.h" +#include "VideoConfig.h" +#include "PixelShaderGen.h" +#include "PixelShaderManager.h" +#include "DX9_PixelShaderCache.h" +#include "VertexLoader.h" +#include "BPMemory.h" +#include "XFMemory.h" +#include "ImageWrite.h" + +#include "../Main.h" + +//#include "Debugger/Debugger.h" + +namespace DX9 +{ + +PixelShaderCache::PSCache PixelShaderCache::PixelShaders; +const PixelShaderCache::PSCacheEntry *PixelShaderCache::last_entry; + +static LinearDiskCache g_ps_disk_cache; +static std::set unique_shaders; + +#define MAX_SSAA_SHADERS 3 + +static LPDIRECT3DPIXELSHADER9 s_ColorMatrixProgram[MAX_SSAA_SHADERS]; +static LPDIRECT3DPIXELSHADER9 s_ColorCopyProgram[MAX_SSAA_SHADERS]; +static LPDIRECT3DPIXELSHADER9 s_DepthMatrixProgram[MAX_SSAA_SHADERS]; +static LPDIRECT3DPIXELSHADER9 s_ClearProgram = 0; + +LPDIRECT3DPIXELSHADER9 PixelShaderCache::GetColorMatrixProgram(int SSAAMode) +{ + return s_ColorMatrixProgram[SSAAMode % MAX_SSAA_SHADERS]; +} + +LPDIRECT3DPIXELSHADER9 PixelShaderCache::GetDepthMatrixProgram(int SSAAMode) +{ + return s_DepthMatrixProgram[SSAAMode % MAX_SSAA_SHADERS]; +} + +LPDIRECT3DPIXELSHADER9 PixelShaderCache::GetColorCopyProgram(int SSAAMode) +{ + return s_ColorCopyProgram[SSAAMode % MAX_SSAA_SHADERS]; +} + +LPDIRECT3DPIXELSHADER9 PixelShaderCache::GetClearProgram() +{ + return s_ClearProgram; +} + +void PixelShaderCache::SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + float f[4] = { f1, f2, f3, f4 }; + D3D::dev->SetPixelShaderConstantF(const_number, f, 1); +} + +void PixelShaderCache::SetPSConstant4fv(unsigned int const_number, const float *f) +{ + D3D::dev->SetPixelShaderConstantF(const_number, f, 1); +} + +void PixelShaderCache::SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float *f) +{ + D3D::dev->SetPixelShaderConstantF(const_number, f, count); +} + +class PixelShaderCacheInserter : public LinearDiskCacheReader { +public: + void Read(const u8 *key, int key_size, const u8 *value, int value_size) + { + PIXELSHADERUID uid; + if (key_size != sizeof(uid)) { + ERROR_LOG(VIDEO, "Wrong key size in pixel shader cache"); + return; + } + memcpy(&uid, key, key_size); + PixelShaderCache::InsertByteCode(uid, value, value_size, false); + } +}; + +PixelShaderCache::PixelShaderCache() +{ + //program used for clear screen + char pprog[3072]; + sprintf(pprog, "void main(\n" + "out float4 ocol0 : COLOR0,\n" + " in float4 incol0 : COLOR0){\n" + "ocol0 = incol0;\n" + "}\n"); + s_ClearProgram = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + //Used for Copy/resolve the color buffer + //1 Sample + sprintf(pprog, "uniform sampler samp0 : register(s0);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float2 uv0 : TEXCOORD0){\n" + "ocol0 = tex2D(samp0,uv0);\n" + "}\n"); + s_ColorCopyProgram[0] = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + //1 Samples SSAA + sprintf(pprog, "uniform sampler samp0 : register(s0);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float4 uv0 : TEXCOORD0,\n" + "in float4 uv1 : TEXCOORD1){\n" + "ocol0 = tex2D(samp0,uv0.xy);\n" + "}\n"); + s_ColorCopyProgram[1] = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + //4 Samples SSAA + sprintf(pprog, "uniform sampler samp0 : register(s0);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float4 uv0 : TEXCOORD0,\n" + "in float4 uv1 : TEXCOORD1,\n" + "in float4 uv2 : TEXCOORD2,\n" + "in float4 uv3 : TEXCOORD3){\n" + "ocol0 = (tex2D(samp0,uv1.xy) + tex2D(samp0,uv1.wz) + tex2D(samp0,uv2.xy) + tex2D(samp0,uv2.wz))*0.25;\n" + "}\n"); + s_ColorCopyProgram[2] = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + + + //Color conversion Programs + //1 sample + sprintf(pprog, "uniform sampler samp0 : register(s0);\n" + "uniform float4 cColMatrix[5] : register(c%d);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + " in float2 uv0 : TEXCOORD0){\n" + "float4 texcol = tex2D(samp0,uv0);\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n",C_COLORMATRIX); + s_ColorMatrixProgram[0] = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + //1 samples SSAA + sprintf(pprog, "uniform sampler samp0 : register(s0);\n" + "uniform float4 cColMatrix[5] : register(c%d);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float4 uv0 : TEXCOORD0,\n" + "in float4 uv1 : TEXCOORD1){\n" + "float4 texcol = tex2D(samp0,uv0.xy);\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n",C_COLORMATRIX); + s_ColorMatrixProgram[1] = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + //4 samples SSAA + sprintf(pprog, "uniform sampler samp0 : register(s0);\n" + "uniform float4 cColMatrix[5] : register(c%d);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float4 uv0 : TEXCOORD0,\n" + "in float4 uv1 : TEXCOORD1,\n" + "in float4 uv2 : TEXCOORD2,\n" + "in float4 uv3 : TEXCOORD3){\n" + "float4 texcol = (tex2D(samp0,uv1.xy) + tex2D(samp0,uv1.wz) + tex2D(samp0,uv2.xy) + tex2D(samp0,uv2.wz))*0.25f;\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n",C_COLORMATRIX); + s_ColorMatrixProgram[2] = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + //Depth copy programs + //1 sample + sprintf(pprog, "uniform sampler samp0 : register(s0);\n" + "uniform float4 cColMatrix[5] : register(c%d);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + " in float2 uv0 : TEXCOORD0){\n" + "float4 texcol = tex2D(samp0,uv0);\n" + "float4 EncodedDepth = frac((texcol.r * (16777215.0f/16777216.0f)) * float4(1.0f,255.0f,255.0f*255.0f,255.0f*255.0f*255.0f));\n" + "texcol = float4((EncodedDepth.rgb * (16777216.0f/16777215.0f)),1.0f);\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n",C_COLORMATRIX); + s_DepthMatrixProgram[0] = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + //1 sample SSAA + sprintf(pprog, "uniform sampler samp0 : register(s0);\n" + "uniform float4 cColMatrix[5] : register(c%d);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float4 uv0 : TEXCOORD0,\n" + "in float4 uv1 : TEXCOORD1){\n" + "float4 texcol = tex2D(samp0,uv0.xy);\n" + "float4 EncodedDepth = frac((texcol.r * (16777215.0f/16777216.0f)) * float4(1.0f,255.0f,255.0f*255.0f,255.0f*255.0f*255.0f));\n" + "texcol = float4((EncodedDepth.rgb * (16777216.0f/16777215.0f)),1.0f);\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n",C_COLORMATRIX); + s_DepthMatrixProgram[1] = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + //4 sample SSAA + sprintf(pprog, "uniform sampler samp0 : register(s0);\n" + "uniform float4 cColMatrix[5] : register(c%d);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float4 uv0 : TEXCOORD0,\n" + "in float4 uv1 : TEXCOORD1,\n" + "in float4 uv2 : TEXCOORD2,\n" + "in float4 uv3 : TEXCOORD3){\n" + "float4 texcol = (tex2D(samp0,uv1.xy) + tex2D(samp0,uv1.wz) + tex2D(samp0,uv2.xy) + tex2D(samp0,uv2.wz))*0.25f;\n" + "float4 EncodedDepth = frac((texcol.r * (16777215.0f/16777216.0f)) * float4(1.0f,255.0f,255.0f*255.0f,255.0f*255.0f*255.0f));\n" + "texcol = float4((EncodedDepth.rgb * (16777216.0f/16777215.0f)),1.0f);\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n",C_COLORMATRIX); + s_DepthMatrixProgram[2] = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); + + Clear(); + + if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) + File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); + + SETSTAT(stats.numPixelShadersCreated, 0); + SETSTAT(stats.numPixelShadersAlive, 0); + + char cache_filename[MAX_PATH]; + sprintf(cache_filename, "%sdx9-%s-ps.cache", File::GetUserPath(D_SHADERCACHE_IDX), g_globals->unique_id); + PixelShaderCacheInserter inserter; + int read_items = g_ps_disk_cache.OpenAndRead(cache_filename, &inserter); +} + +// ONLY to be used during shutdown. +void PixelShaderCache::Clear() +{ + PSCache::iterator iter = PixelShaders.begin(); + for (; iter != PixelShaders.end(); ++iter) + iter->second.Destroy(); + PixelShaders.clear(); + + memset(&last_pixel_shader_uid, 0xFF, sizeof(last_pixel_shader_uid)); +} + +PixelShaderCache::~PixelShaderCache() +{ + for(int i = 0;i < MAX_SSAA_SHADERS; i++) + { + if (s_ColorMatrixProgram[i]) s_ColorMatrixProgram[i]->Release(); + s_ColorMatrixProgram[i] = NULL; + if (s_ColorCopyProgram[i]) s_ColorCopyProgram[i]->Release(); + s_ColorCopyProgram[i] = NULL; + if (s_DepthMatrixProgram[i]) s_DepthMatrixProgram[i]->Release(); + s_DepthMatrixProgram[i] = NULL; + } + if (s_ClearProgram) s_ClearProgram->Release(); + s_ClearProgram = NULL; + + Clear(); + g_ps_disk_cache.Sync(); + g_ps_disk_cache.Close(); + + unique_shaders.clear(); +} + +bool PixelShaderCache::SetShader(bool dstAlpha) +{ + PIXELSHADERUID uid; + GetPixelShaderId(&uid, dstAlpha); + + // Is the shader already set? + if (uid == last_pixel_shader_uid && PixelShaders[uid].frameCount == frameCount) + { + PSCache::const_iterator iter = PixelShaders.find(uid); + if (iter != PixelShaders.end() && iter->second.shader) + return true; // Sure, we're done. + else + return false; // ?? something is wrong. + } + + memcpy(&last_pixel_shader_uid, &uid, sizeof(PIXELSHADERUID)); + + // Is the shader already in the cache? + PSCache::iterator iter; + iter = PixelShaders.find(uid); + if (iter != PixelShaders.end()) + { + iter->second.frameCount = frameCount; + const PSCacheEntry &entry = iter->second; + last_entry = &entry; + + if (entry.shader) + { + D3D::SetPixelShader(entry.shader); + return true; + } + else + return false; + } + + // OK, need to generate and compile it. + const char *code = GeneratePixelShaderCode(dstAlpha, API_D3D9); + + u32 code_hash = HashAdler32((const u8 *)code, strlen(code)); + unique_shaders.insert(code_hash); + SETSTAT(stats.numUniquePixelShaders, unique_shaders.size()); + + #if defined(_DEBUG) || defined(DEBUGFAST) + if (g_ActiveConfig.iLog & CONF_SAVESHADERS && code) { + static int counter = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX), counter++); + + SaveData(szTemp, code); + } + #endif + + u8 *bytecode = 0; + int bytecodelen = 0; + if (!D3D::CompilePixelShader(code, (int)strlen(code), &bytecode, &bytecodelen)) { + if (g_ActiveConfig.bShowShaderErrors) + { + PanicAlert("Failed to compile Pixel Shader:\n\n%s", code); + static int counter = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%sBADps_%04i.txt", File::GetUserPath(D_DUMP_IDX), counter++); + SaveData(szTemp, code); + } + return false; + } + + // Here we have the UID and the byte code. Insert it into the disk cache. + g_ps_disk_cache.Append((u8 *)&uid, sizeof(uid), bytecode, bytecodelen); + g_ps_disk_cache.Sync(); + + // And insert it into the shader cache. + bool result = InsertByteCode(uid, bytecode, bytecodelen, true); + delete [] bytecode; + return result; +} + +bool PixelShaderCache::InsertByteCode(const PIXELSHADERUID &uid, const u8 *bytecode, int bytecodelen, bool activate) { + LPDIRECT3DPIXELSHADER9 shader = D3D::CreatePixelShaderFromByteCode(bytecode, bytecodelen); + + // Make an entry in the table + PSCacheEntry newentry; + newentry.shader = shader; + newentry.frameCount = frameCount; + PixelShaders[uid] = newentry; + last_entry = &PixelShaders[uid]; + + if (!shader) { + // INCSTAT(stats.numPixelShadersFailed); + return false; + } + + INCSTAT(stats.numPixelShadersCreated); + SETSTAT(stats.numPixelShadersAlive, (int)PixelShaders.size()); + if (activate) + { + D3D::SetPixelShader(shader); + } + return true; +} + + +#if defined(_DEBUG) || defined(DEBUGFAST) +std::string PixelShaderCache::GetCurrentShaderCode() +{ + if (last_entry) + return last_entry->code; + else + return "(no shader)\n"; +} +#endif + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_PixelShaderCache.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_PixelShaderCache.h new file mode 100644 index 0000000000..7506b3466b --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_PixelShaderCache.h @@ -0,0 +1,89 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _PIXELSHADERCACHE_H +#define _PIXELSHADERCACHE_H + +#include "Common.h" +#include "LinearDiskCache.h" +#include "DX9_D3DBase.h" + +#include + +#include "PixelShaderGen.h" +#include "VertexShaderGen.h" + +#include "../PixelShaderCache.h" + +namespace DX9 +{ + +typedef u32 tevhash; + +tevhash GetCurrentTEV(); + +class PixelShaderCache : public ::PixelShaderCacheBase +{ +private: + struct PSCacheEntry + { + LPDIRECT3DPIXELSHADER9 shader; + bool owns_shader; + int frameCount; +#if defined(_DEBUG) || defined(DEBUGFAST) + std::string code; +#endif + PSCacheEntry() : shader(NULL), owns_shader(true), frameCount(0) {} + void Destroy() + { + if (shader && owns_shader) + shader->Release(); + shader = NULL; + } + }; + + typedef std::map PSCache; + + static PSCache PixelShaders; + static const PSCacheEntry *last_entry; + static void Clear(); + +public: + PixelShaderCache(); + ~PixelShaderCache(); + + void SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4); + void SetPSConstant4fv(unsigned int const_number, const float* f); + void SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float* f); + + bool SetShader(bool dstAlpha); + + static bool InsertByteCode(const PIXELSHADERUID &uid, const u8 *bytecode, int bytecodelen, bool activate); + static LPDIRECT3DPIXELSHADER9 GetColorMatrixProgram(int SSAAMode); + static LPDIRECT3DPIXELSHADER9 GetColorCopyProgram(int SSAAMode); + static LPDIRECT3DPIXELSHADER9 GetDepthMatrixProgram(int SSAAMode); + static LPDIRECT3DPIXELSHADER9 GetClearProgram(); + +#if defined(_DEBUG) || defined(DEBUGFAST) + static std::string GetCurrentShaderCode(); +#endif + static LPDIRECT3DPIXELSHADER9 CompileCgShader(const char *pstrprogram); +}; + +} + +#endif // _PIXELSHADERCACHE_H diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_Render.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_Render.cpp new file mode 100644 index 0000000000..7094d35c00 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_Render.cpp @@ -0,0 +1,960 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include + +#include "StringUtil.h" +#include "Common.h" +#include "Atomic.h" +#include "FileUtil.h" +#include "Thread.h" +#include "Timer.h" +#include "Statistics.h" + +#include "VideoConfig.h" +#include "main.h" +#include "DX9_VertexManager.h" +#include "DX9_Render.h" +#include "OpcodeDecoding.h" +#include "BPStructs.h" +#include "XFStructs.h" +#include "DX9_D3DUtil.h" +#include "VertexShaderManager.h" +#include "PixelShaderManager.h" +#include "DX9_VertexShaderCache.h" +#include "DX9_PixelShaderCache.h" +#include "VertexLoaderManager.h" +#include "DX9_TextureCache.h" +#include "EmuWindow.h" +#include "AVIDump.h" +#include "OnScreenDisplay.h" +#include "DX9_FramebufferManager.h" +#include "Fifo.h" +#include "DX9_TextureConverter.h" +#include "DLCache.h" + +//#include "debugger/debugger.h" + +#include "DX9_Render.h" + +namespace DX9 +{ + +bool Renderer::IS_AMD; + +// State translation lookup tables +static const D3DBLEND d3dSrcFactors[8] = +{ + D3DBLEND_ZERO, + D3DBLEND_ONE, + D3DBLEND_DESTCOLOR, + D3DBLEND_INVDESTCOLOR, + D3DBLEND_SRCALPHA, + D3DBLEND_INVSRCALPHA, + D3DBLEND_DESTALPHA, + D3DBLEND_INVDESTALPHA +}; + +static const D3DBLEND d3dDestFactors[8] = +{ + D3DBLEND_ZERO, + D3DBLEND_ONE, + D3DBLEND_SRCCOLOR, + D3DBLEND_INVSRCCOLOR, + D3DBLEND_SRCALPHA, + D3DBLEND_INVSRCALPHA, + D3DBLEND_DESTALPHA, + D3DBLEND_INVDESTALPHA +}; + +static const D3DBLENDOP d3dLogicOpop[16] = +{ + D3DBLENDOP_ADD, + D3DBLENDOP_ADD, + D3DBLENDOP_SUBTRACT, + D3DBLENDOP_ADD, + D3DBLENDOP_REVSUBTRACT, + D3DBLENDOP_ADD, + D3DBLENDOP_MAX, + D3DBLENDOP_ADD, + + D3DBLENDOP_MAX, + D3DBLENDOP_MAX, + D3DBLENDOP_ADD, + D3DBLENDOP_ADD, + D3DBLENDOP_ADD, + D3DBLENDOP_ADD, + D3DBLENDOP_ADD, + D3DBLENDOP_ADD +}; + +static const D3DBLEND d3dLogicOpSrcFactors[16] = +{ + D3DBLEND_ZERO, + D3DBLEND_DESTCOLOR, + D3DBLEND_ONE, + D3DBLEND_ONE, + D3DBLEND_DESTCOLOR, + D3DBLEND_ZERO, + D3DBLEND_INVDESTCOLOR, + D3DBLEND_INVDESTCOLOR, + + D3DBLEND_INVSRCCOLOR, + D3DBLEND_INVSRCCOLOR, + D3DBLEND_INVDESTCOLOR, + D3DBLEND_ONE, + D3DBLEND_INVSRCCOLOR, + D3DBLEND_INVSRCCOLOR, + D3DBLEND_INVDESTCOLOR, + D3DBLEND_ONE +}; + +static const D3DBLEND d3dLogicOpDestFactors[16] = +{ + D3DBLEND_ZERO, + D3DBLEND_ZERO, + D3DBLEND_INVSRCCOLOR, + D3DBLEND_ZERO, + D3DBLEND_ONE, + D3DBLEND_ONE, + D3DBLEND_INVSRCCOLOR, + D3DBLEND_ONE, + + D3DBLEND_INVDESTCOLOR, + D3DBLEND_SRCCOLOR, + D3DBLEND_INVDESTCOLOR, + D3DBLEND_INVDESTCOLOR, + D3DBLEND_INVSRCCOLOR, + D3DBLEND_ONE, + D3DBLEND_INVSRCCOLOR, + D3DBLEND_ONE +}; + +static const D3DCULL d3dCullModes[4] = +{ + D3DCULL_NONE, + D3DCULL_CCW, + D3DCULL_CW, + D3DCULL_CCW +}; + +static const D3DCMPFUNC d3dCmpFuncs[8] = +{ + D3DCMP_NEVER, + D3DCMP_LESS, + D3DCMP_EQUAL, + D3DCMP_LESSEQUAL, + D3DCMP_GREATER, + D3DCMP_NOTEQUAL, + D3DCMP_GREATEREQUAL, + D3DCMP_ALWAYS +}; + +static const D3DTEXTUREFILTERTYPE d3dMipFilters[4] = +{ + D3DTEXF_NONE, + D3DTEXF_POINT, + D3DTEXF_LINEAR, + D3DTEXF_NONE, //reserved +}; + +static const D3DTEXTUREADDRESS d3dClamps[4] = +{ + D3DTADDRESS_CLAMP, + D3DTADDRESS_WRAP, + D3DTADDRESS_MIRROR, + D3DTADDRESS_WRAP //reserved +}; + +void Renderer::SetupDeviceObjects() +{ + D3D::font.Init(); + g_framebuffer_manager = new FramebufferManager; + + VertexShaderManager::Dirty(); + PixelShaderManager::Dirty(); + // TODO: + //TextureConverter::Init(); + + // To avoid shader compilation stutters, read back all shaders from cache. + // Texture cache will recreate themselves over time. +} + +// Kill off all POOL_DEFAULT device objects. +void Renderer::TeardownDeviceObjects() +{ + D3D::dev->SetRenderTarget(0, D3D::GetBackBufferSurface()); + D3D::dev->SetDepthStencilSurface(D3D::GetBackBufferDepthSurface()); + + delete g_framebuffer_manager; + + //D3D::font.Shutdown(); + // TODO: + //TextureConverter::Shutdown(); +} + +Renderer::Renderer() +{ + UpdateActiveConfig(); + int fullScreenRes, x, y, w_temp, h_temp; + s_blendMode = 0; + // Multisample Anti-aliasing hasn't been implemented yet use supersamling instead + int backbuffer_ms_mode = 0; + + g_VideoInitialize.pRequestWindowSize(x, y, w_temp, h_temp); + + for (fullScreenRes = 0; fullScreenRes < (int)D3D::GetAdapter(g_ActiveConfig.iAdapter).resolutions.size(); fullScreenRes++) + { + if ((D3D::GetAdapter(g_ActiveConfig.iAdapter).resolutions[fullScreenRes].xres == w_temp) && + (D3D::GetAdapter(g_ActiveConfig.iAdapter).resolutions[fullScreenRes].yres == h_temp)) + break; + } + if (fullScreenRes == D3D::GetAdapter(g_ActiveConfig.iAdapter).resolutions.size()) + fullScreenRes = 0; + + D3D::Init(); + + D3D::Create(g_ActiveConfig.iAdapter, EmuWindow::GetWnd(), + fullScreenRes, backbuffer_ms_mode, false); + + IS_AMD = D3D::IsATIDevice(); + s_backbuffer_width = D3D::GetBackBufferWidth(); + s_backbuffer_height = D3D::GetBackBufferHeight(); + + s_XFB_width = MAX_XFB_WIDTH; + s_XFB_height = MAX_XFB_HEIGHT; + + FramebufferSize(s_backbuffer_width, s_backbuffer_height); + + // We're not using fixed function. + // Let's just set the matrices to identity to be sure. + D3DXMATRIX mtx; + D3DXMatrixIdentity(&mtx); + D3D::dev->SetTransform(D3DTS_VIEW, &mtx); + D3D::dev->SetTransform(D3DTS_WORLD, &mtx); + + SetupDeviceObjects(); + + for (int stage = 0; stage < 8; stage++) + D3D::SetSamplerState(stage, D3DSAMP_MAXANISOTROPY, g_ActiveConfig.iMaxAnisotropy); + + D3DVIEWPORT9 vp; + vp.X = 0; + vp.Y = 0; + vp.Width = s_backbuffer_width; + vp.Height = s_backbuffer_height; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + D3D::dev->SetViewport(&vp); + D3D::dev->Clear(0, NULL, D3DCLEAR_TARGET, 0x0, 0, 0); + + D3D::dev->SetRenderTarget(0, FramebufferManager::GetEFBColorRTSurface()); + D3D::dev->SetDepthStencilSurface(FramebufferManager::GetEFBDepthRTSurface()); + vp.X = (s_Fulltarget_width - s_target_width) / 2; + vp.Y = (s_Fulltarget_height - s_target_height) / 2; + vp.Width = s_target_width; + vp.Height = s_target_height; + D3D::dev->SetViewport(&vp); + D3D::dev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x0, 1.0f, 0); + D3D::BeginFrame(); + D3D::SetRenderState(D3DRS_SCISSORTESTENABLE, true); + //return true; +} + +Renderer::~Renderer() +{ + TeardownDeviceObjects(); + D3D::EndFrame(); + D3D::Present(); + D3D::Close(); +} + +void formatBufferDump(const char *in, char *out, int w, int h, int p) +{ + for (int y = 0; y < h; y++) + { + const char *line = in + (h - y - 1) * p; + for (int x = 0; x < w; x++) + { + memcpy(out, line, 3); + out += 3; + line += 4; + } + } +} + +// With D3D, we have to resize the backbuffer if the window changed +// size. +bool Renderer::CheckForResize() +{ + while (EmuWindow::IsSizing()) + { + Sleep(10); + } + + if (EmuWindow::GetParentWnd()) + { + // Re-stretch window to parent window size again, if it has a parent window. + RECT rcParentWindow; + GetWindowRect(EmuWindow::GetParentWnd(), &rcParentWindow); + int width = rcParentWindow.right - rcParentWindow.left; + int height = rcParentWindow.bottom - rcParentWindow.top; + if (width != s_backbuffer_width || height != s_backbuffer_height) + MoveWindow(EmuWindow::GetWnd(), 0, 0, width, height, FALSE); + } + RECT rcWindow; + GetClientRect(EmuWindow::GetWnd(), &rcWindow); + int client_width = rcWindow.right - rcWindow.left; + int client_height = rcWindow.bottom - rcWindow.top; + // Sanity check. + if ((client_width != s_backbuffer_width || + client_height != s_backbuffer_height) && + client_width >= 4 && client_height >= 4) + { + TeardownDeviceObjects(); + + D3D::Reset(); + s_backbuffer_width = D3D::GetBackBufferWidth(); + s_backbuffer_height = D3D::GetBackBufferHeight(); + + return true; + } + + return false; +} + +bool Renderer::SetScissorRect() +{ + EFBRectangle rc; + if (g_renderer->SetScissorRect(rc)) + { + D3D::dev->SetScissorRect((RECT*)&rc); + return true; + } + else + { + //WARN_LOG(VIDEO, "Bad scissor rectangle: %i %i %i %i", rc.left, rc.top, rc.right, rc.bottom); + const int Xstride = (s_Fulltarget_width - s_target_width) / 2; + const int Ystride = (s_Fulltarget_height - s_target_height) / 2; + rc.left = Xstride; + rc.top = Ystride; + rc.right = Xstride + s_target_width; + rc.bottom = Ystride + s_target_height; + + D3D::dev->SetScissorRect((RECT*)&rc); + return false; + } +} + +void Renderer::SetColorMask() +{ + DWORD color_mask = 0; + if (bpmem.blendmode.alphaupdate) + color_mask = D3DCOLORWRITEENABLE_ALPHA; + if (bpmem.blendmode.colorupdate) + color_mask |= D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE; + D3D::SetRenderState(D3DRS_COLORWRITEENABLE, color_mask); +} + +u32 Renderer::AccessEFB(EFBAccessType type, int x, int y) +{ + if (!g_ActiveConfig.bEFBAccessEnable) + return 0; + + if (type == POKE_Z || type == POKE_COLOR) + { + static bool alert_only_once = true; + if (!alert_only_once) return 0; + PanicAlert("Poke EFB not implemented"); + alert_only_once = false; + return 0; + } + + // Get the working buffer + LPDIRECT3DSURFACE9 pBuffer = (type == PEEK_Z || type == POKE_Z) ? + FramebufferManager::GetEFBDepthRTSurface() : FramebufferManager::GetEFBColorRTSurface(); + // Get the temporal buffer to move 1pixel data + LPDIRECT3DSURFACE9 RBuffer = (type == PEEK_Z || type == POKE_Z) ? + FramebufferManager::GetEFBDepthReadSurface() : FramebufferManager::GetEFBColorReadSurface(); + // Get the memory buffer that can be locked + LPDIRECT3DSURFACE9 pOffScreenBuffer = (type == PEEK_Z || type == POKE_Z) ? + FramebufferManager::GetEFBDepthOffScreenRTSurface() : FramebufferManager::GetEFBColorOffScreenRTSurface(); + // Get the buffer format + D3DFORMAT BufferFormat = (type == PEEK_Z || type == POKE_Z) ? + FramebufferManager::GetEFBDepthRTSurfaceFormat() : FramebufferManager::GetEFBColorRTSurfaceFormat(); + D3DFORMAT ReadBufferFormat = (type == PEEK_Z || type == POKE_Z) ? + FramebufferManager::GetEFBDepthReadSurfaceFormat() : BufferFormat; + + if (BufferFormat == D3DFMT_D24X8) + return 0; + + D3DLOCKED_RECT drect; + + // Buffer not found alert + if (!pBuffer) { + PanicAlert("No %s!", (type == PEEK_Z || type == POKE_Z) ? "Z-Buffer" : "Color EFB"); + return 0; + } + // Get the rectangular target region covered by the EFB pixel. + + EFBRectangle efbPixelRc; + efbPixelRc.left = x; + efbPixelRc.top = y; + efbPixelRc.right = x + 1; + efbPixelRc.bottom = y + 1; + + TargetRectangle targetPixelRc = Renderer::ConvertEFBRectangle(efbPixelRc); + + u32 z = 0; + float val = 0.0f; + HRESULT hr; + RECT RectToLock; + RectToLock.bottom = targetPixelRc.bottom; + RectToLock.left = targetPixelRc.left; + RectToLock.right = targetPixelRc.right; + RectToLock.top = targetPixelRc.top; + if (type == PEEK_Z) + { + RECT PixelRect; + PixelRect.bottom = 4; + PixelRect.left = 0; + PixelRect.right = 4; + PixelRect.top = 0; + RectToLock.bottom+=2; + RectToLock.right+=1; + RectToLock.top-=1; + RectToLock.left-=2; + if ((RectToLock.bottom - RectToLock.top) > 4) + RectToLock.bottom--; + if ((RectToLock.right - RectToLock.left) > 4) + RectToLock.left++; + ResetAPIState(); // Reset any game specific settings + hr = D3D::dev->SetDepthStencilSurface(NULL); + hr = D3D::dev->SetRenderTarget(0, RBuffer); + if (FAILED(hr)) + { + PanicAlert("unable to set pixel render buffer"); + return 0; + } + D3DVIEWPORT9 vp; + // Stretch picture with increased internal resolution + vp.X = 0; + vp.Y = 0; + vp.Width = 4; + vp.Height = 4; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + hr = D3D::dev->SetViewport(&vp); + if (FAILED(hr)) + { + PanicAlert("unable to set pixel viewport"); + return 0; + } + float colmat[16] = {0.0f}; + float fConstAdd[4] = {0.0f}; + colmat[0] = colmat[5] = colmat[10] = 1.0f; + PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation + EFBRectangle source_rect; + LPDIRECT3DTEXTURE9 read_texture = FramebufferManager::GetEFBDepthTexture(source_rect); + + D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + + D3D::drawShadedTexQuad( + read_texture, + &RectToLock, + Renderer::GetFullTargetWidth(), + Renderer::GetFullTargetHeight(), + 4, 4, + (BufferFormat == FOURCC_RAWZ) ? PixelShaderCache::GetColorMatrixProgram(0) : PixelShaderCache::GetDepthMatrixProgram(0), + VertexShaderCache::GetSimpleVertexShader(0)); + + D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER); + + hr = D3D::dev->SetRenderTarget(0, FramebufferManager::GetEFBColorRTSurface()); + hr = D3D::dev->SetDepthStencilSurface(FramebufferManager::GetEFBDepthRTSurface()); + RestoreAPIState(); + RectToLock.bottom = 4; + RectToLock.left = 0; + RectToLock.right = 4; + RectToLock.top = 0; + } + else + { + hr = D3D::dev->StretchRect(pBuffer, &RectToLock, RBuffer, NULL, D3DTEXF_NONE); + //change the rect to lock the entire one pixel buffer + RectToLock.bottom = 1; + RectToLock.left = 0; + RectToLock.right = 1; + RectToLock.top = 0; + } + if (FAILED(hr)) + { + PanicAlert("Unable to stretch data to buffer"); + return 0; + } + // Retrieve the pixel data to the local memory buffer + D3D::dev->GetRenderTargetData(RBuffer, pOffScreenBuffer); + if (FAILED(hr)) + { + PanicAlert("Unable to copy data to mem buffer"); + return 0; + } + + + + // The surface is good.. lock it + if ((hr = pOffScreenBuffer->LockRect(&drect, &RectToLock, D3DLOCK_READONLY)) != D3D_OK) + { + PanicAlert("ERROR: %s", hr == D3DERR_WASSTILLDRAWING ? "Still drawing" : hr == D3DERR_INVALIDCALL ? "Invalid call" : "w00t"); + return 0; + } + + switch (type) { + case PEEK_Z: + { + switch (ReadBufferFormat) + { + case D3DFMT_R32F: + val = ((float*)drect.pBits)[6]; + break; + default: + float ffrac = 1.0f/255.0f; + z = ((u32*)drect.pBits)[6]; + val = ((float)((z>>16) & 0xFF)) * ffrac; + ffrac*= 1 / 255.0f; + val += ((float)((z>>8) & 0xFF)) * ffrac; + ffrac*= 1 / 255.0f; + val += ((float)(z & 0xFF)) * ffrac; + break; + }; + z = ((u32)(val * 0xffffff)); + } + break; + + case PEEK_COLOR: + z = ((u32 *)drect.pBits)[0]; + break; + case POKE_COLOR: + + // TODO: Implement POKE_Z and POKE_COLOR + default: + break; + } + + pOffScreenBuffer->UnlockRect(); + // TODO: in RE0 this value is often off by one, which causes lighting to disappear + return z; +} + +// Called from VertexShaderManager +void Renderer::UpdateViewport() +{ + // reversed gxsetviewport(xorig, yorig, width, height, nearz, farz) + // [0] = width/2 + // [1] = height/2 + // [2] = 16777215 * (farz - nearz) + // [3] = xorig + width/2 + 342 + // [4] = yorig + height/2 + 342 + // [5] = 16777215 * farz + const int old_fulltarget_w = s_Fulltarget_width; + const int old_fulltarget_h = s_Fulltarget_height; + + int scissorXOff = bpmem.scissorOffset.x * 2; + int scissorYOff = bpmem.scissorOffset.y * 2; + + float MValueX = Renderer::GetTargetScaleX(); + float MValueY = Renderer::GetTargetScaleY(); + + int Xstride = (s_Fulltarget_width - s_target_width) / 2; + int Ystride = (s_Fulltarget_height - s_target_height) / 2; + + D3DVIEWPORT9 vp; + + // Stretch picture with increased internal resolution + int X = (int)(ceil(xfregs.rawViewport[3] - xfregs.rawViewport[0] - (scissorXOff)) * MValueX) + Xstride; + int Y = (int)(ceil(xfregs.rawViewport[4] + xfregs.rawViewport[1] - (scissorYOff)) * MValueY) + Ystride; + int Width = (int)ceil((int)(2 * xfregs.rawViewport[0]) * MValueX); + int Height = (int)ceil((int)(-2 * xfregs.rawViewport[1]) * MValueY); + if (Width < 0) + { + X += Width; + Width*=-1; + } + if (Height < 0) + { + Y += Height; + Height *= -1; + } + bool sizeChanged = false; + if (X < 0) + { + s_Fulltarget_width -= 2 * X; + X = 0; + sizeChanged=true; + } + if (Y < 0) + { + s_Fulltarget_height -= 2 * Y; + Y = 0; + sizeChanged = true; + } + if (!IS_AMD) + { + if(X + Width > s_Fulltarget_width) + { + s_Fulltarget_width += (X + Width - s_Fulltarget_width) * 2; + sizeChanged = true; + } + if(Y + Height > s_Fulltarget_height) + { + s_Fulltarget_height += (Y + Height - s_Fulltarget_height) * 2; + sizeChanged = true; + } + } + if (sizeChanged) + { + D3DCAPS9 caps = D3D::GetCaps(); + // Make sure that the requested size is actually supported by the GFX driver + if (s_Fulltarget_width > caps.MaxTextureWidth || s_Fulltarget_height > caps.MaxTextureHeight) + { + // Skip EFB recreation and viewport setting. Most likely causes glitches in this case, but prevents crashes at least + ERROR_LOG(VIDEO, "Tried to set a viewport which is too wide to emulate with Direct3D9. Requested EFB size is %dx%d, keeping the %dx%d EFB now\n", s_Fulltarget_width, s_Fulltarget_height, old_fulltarget_w, old_fulltarget_h); + + // Fix the viewport to fit to the old EFB size, TODO: Check this for off-by-one errors + X *= old_fulltarget_w / s_Fulltarget_width; + Y *= old_fulltarget_h / s_Fulltarget_height; + Width *= old_fulltarget_w / s_Fulltarget_width; + Height *= old_fulltarget_h / s_Fulltarget_height; + + s_Fulltarget_width = old_fulltarget_w; + s_Fulltarget_height = old_fulltarget_h; + } + else + { + D3D::dev->SetRenderTarget(0, D3D::GetBackBufferSurface()); + D3D::dev->SetDepthStencilSurface(D3D::GetBackBufferDepthSurface()); + + delete g_framebuffer_manager; + g_framebuffer_manager = new FramebufferManager(); + + D3D::dev->SetRenderTarget(0, FramebufferManager::GetEFBColorRTSurface()); + D3D::dev->SetDepthStencilSurface(FramebufferManager::GetEFBDepthRTSurface()); + } + } + vp.X = X; + vp.Y = Y; + vp.Width = Width; + vp.Height = Height; + + // Some games set invalids values for z min and z max so fix them to the max an min alowed and let the shaders do this work + vp.MinZ = 0.0f; // (xfregs.rawViewport[5] - xfregs.rawViewport[2]) / 16777216.0f; + vp.MaxZ = 1.0f; // xfregs.rawViewport[5] / 16777216.0f; + D3D::dev->SetViewport(&vp); +} + +void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) +{ + // Update the view port for clearing the picture + TargetRectangle targetRc = Renderer::ConvertEFBRectangle(rc); + D3DVIEWPORT9 vp; + vp.X = targetRc.left; + vp.Y = targetRc.top; + vp.Width = targetRc.GetWidth(); + vp.Height = targetRc.GetHeight(); + vp.MinZ = 0.0; + vp.MaxZ = 1.0; + D3D::dev->SetViewport(&vp); + + // Always set the scissor in case it was set by the game and has not been reset + RECT sicr; + sicr.left = targetRc.left; + sicr.top = targetRc.top; + sicr.right = targetRc.right; + sicr.bottom = targetRc.bottom; + D3D::dev->SetScissorRect(&sicr); + D3D::ChangeRenderState(D3DRS_ALPHABLENDENABLE, false); + if (zEnable) + D3D::ChangeRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS); + D3D::drawClearQuad(color, (z & 0xFFFFFF) / float(0xFFFFFF), PixelShaderCache::GetClearProgram(), VertexShaderCache::GetClearVertexShader()); + if (zEnable) + D3D::RefreshRenderState(D3DRS_ZFUNC); + D3D::RefreshRenderState(D3DRS_ALPHABLENDENABLE); + UpdateViewport(); + SetScissorRect(); +} + +void Renderer::SetBlendMode(bool forceUpdate) +{ + if (bpmem.blendmode.logicopenable) + return; + + if (bpmem.blendmode.subtract && bpmem.blendmode.blendenable) + { + D3D::SetRenderState(D3DRS_ALPHABLENDENABLE, true); + D3D::SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT); + D3D::SetRenderState(D3DRS_SRCBLEND, d3dSrcFactors[1]); + D3D::SetRenderState(D3DRS_DESTBLEND, d3dDestFactors[1]); + } + else + { + D3D::SetRenderState(D3DRS_ALPHABLENDENABLE, bpmem.blendmode.blendenable && (!( bpmem.blendmode.srcfactor == 1 && bpmem.blendmode.dstfactor == 0))); + if (bpmem.blendmode.blendenable && (!( bpmem.blendmode.srcfactor == 1 && bpmem.blendmode.dstfactor == 0))) + { + D3D::SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + D3D::SetRenderState(D3DRS_SRCBLEND, d3dSrcFactors[bpmem.blendmode.srcfactor]); + D3D::SetRenderState(D3DRS_DESTBLEND, d3dDestFactors[bpmem.blendmode.dstfactor]); + } + } +} + +void Renderer::ResetAPIState() +{ + D3D::SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); + D3D::SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + D3D::SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + D3D::SetRenderState(D3DRS_ZENABLE, FALSE); + D3D::SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + DWORD color_mask = D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE; + D3D::SetRenderState(D3DRS_COLORWRITEENABLE, color_mask); +} + +void Renderer::RestoreAPIState() +{ + // Gets us back into a more game-like state. + D3D::SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + UpdateViewport(); + SetScissorRect(); + if (bpmem.zmode.testenable) + D3D::SetRenderState(D3DRS_ZENABLE, TRUE); + if (bpmem.zmode.updateenable) + D3D::SetRenderState(D3DRS_ZWRITEENABLE, TRUE); + SetColorMask(); + SetLogicOpMode(); +} + +void Renderer::SetGenerationMode() +{ + D3D::SetRenderState(D3DRS_CULLMODE, d3dCullModes[bpmem.genMode.cullmode]); +} + +void Renderer::SetDepthMode() +{ + if (bpmem.zmode.testenable) + { + D3D::SetRenderState(D3DRS_ZENABLE, TRUE); + D3D::SetRenderState(D3DRS_ZWRITEENABLE, bpmem.zmode.updateenable); + D3D::SetRenderState(D3DRS_ZFUNC, d3dCmpFuncs[bpmem.zmode.func]); + } + else + { + D3D::SetRenderState(D3DRS_ZENABLE, FALSE); + D3D::SetRenderState(D3DRS_ZWRITEENABLE, FALSE); // ?? + } +} + +void Renderer::SetLogicOpMode() +{ + if (bpmem.blendmode.logicopenable && bpmem.blendmode.logicmode != 3) + { + D3D::SetRenderState(D3DRS_ALPHABLENDENABLE, true); + D3D::SetRenderState(D3DRS_BLENDOP, d3dLogicOpop[bpmem.blendmode.logicmode]); + D3D::SetRenderState(D3DRS_SRCBLEND, d3dLogicOpSrcFactors[bpmem.blendmode.logicmode]); + D3D::SetRenderState(D3DRS_DESTBLEND, d3dLogicOpDestFactors[bpmem.blendmode.logicmode]); + } + else + { + SetBlendMode(true); + } +} + +void Renderer::SetDitherMode() +{ + D3D::SetRenderState(D3DRS_DITHERENABLE, bpmem.blendmode.dither); +} + +void Renderer::SetLineWidth() +{ + // We can't change line width in D3D unless we use ID3DXLine + float fratio = xfregs.rawViewport[0] != 0 ? Renderer::GetTargetScaleX() : 1.0f; + float psize = bpmem.lineptwidth.linesize * fratio / 6.0f; + D3D::SetRenderState(D3DRS_POINTSIZE, *((DWORD*)&psize)); +} + +void Renderer::SetSamplerState(int stage, int texindex) +{ + const FourTexUnits &tex = bpmem.tex[texindex]; + const TexMode0 &tm0 = tex.texMode0[stage]; + const TexMode1 &tm1 = tex.texMode1[stage]; + + D3DTEXTUREFILTERTYPE min, mag, mip; + if (g_ActiveConfig.bForceFiltering) + { + min = mag = mip = D3DTEXF_LINEAR; + } + else + { + min = (tm0.min_filter & 4) ? D3DTEXF_LINEAR : D3DTEXF_POINT; + mag = tm0.mag_filter ? D3DTEXF_LINEAR : D3DTEXF_POINT; + mip = (tm0.min_filter == 8) ? D3DTEXF_NONE : d3dMipFilters[tm0.min_filter & 3]; + if((tm0.min_filter & 3) && (tm0.min_filter != 8) && ((tm1.max_lod >> 4) == 0)) + mip = D3DTEXF_NONE; + } + if (texindex) + stage += 4; + + if (mag == D3DTEXF_LINEAR && min == D3DTEXF_LINEAR && g_ActiveConfig.iMaxAnisotropy > 1) + { + min = D3DTEXF_ANISOTROPIC; + } + D3D::SetSamplerState(stage, D3DSAMP_MINFILTER, min); + D3D::SetSamplerState(stage, D3DSAMP_MAGFILTER, mag); + D3D::SetSamplerState(stage, D3DSAMP_MIPFILTER, mip); + + D3D::SetSamplerState(stage, D3DSAMP_ADDRESSU, d3dClamps[tm0.wrap_s]); + D3D::SetSamplerState(stage, D3DSAMP_ADDRESSV, d3dClamps[tm0.wrap_t]); + //float SuperSampleCoeficient = (s_LastAA < 3)? s_LastAA + 1 : s_LastAA - 1;// uncoment this changes to conserve detail when incresing ssaa level + float lodbias = (tm0.lod_bias / 32.0f);// + (s_LastAA)?(log(SuperSampleCoeficient) / log(2.0f)):0; + D3D::SetSamplerState(stage, D3DSAMP_MIPMAPLODBIAS, *(DWORD*)&lodbias); + D3D::SetSamplerState(stage, D3DSAMP_MAXMIPLEVEL, tm1.min_lod >> 4); +} + +void Renderer::PrepareXFBCopy(const TargetRectangle &dst_rect) +{ + D3D::dev->SetDepthStencilSurface(NULL); + D3D::dev->SetRenderTarget(0, D3D::GetBackBufferSurface()); + + D3DVIEWPORT9 vp; + vp.X = 0; + vp.Y = 0; + vp.Width = s_backbuffer_width; + vp.Height = s_backbuffer_height; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + D3D::dev->SetViewport(&vp); + D3D::dev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); + + int X = dst_rect.left; + int Y = dst_rect.top; + int Width = dst_rect.right - dst_rect.left; + int Height = dst_rect.bottom - dst_rect.top; + + if (X < 0) X = 0; + if (Y < 0) Y = 0; + if (X > s_backbuffer_width) X = s_backbuffer_width; + if (Y > s_backbuffer_height) Y = s_backbuffer_height; + if (Width < 0) Width = 0; + if (Height < 0) Height = 0; + if (Width > (s_backbuffer_width - X)) Width = s_backbuffer_width - X; + if (Height > (s_backbuffer_height - Y)) Height = s_backbuffer_height - Y; + vp.X = X; + vp.Y = Y; + vp.Width = Width; + vp.Height = Height; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + + D3D::dev->SetViewport(&vp); + + D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); +} + +void Renderer::Draw(const XFBSourceBase* xfbSource, const TargetRectangle& sourceRc, + const MathUtil::Rectangle& drawRc, const EFBRectangle& rc) +{ + // TODO: this is lame here + TargetRectangle dst_rect; + ComputeDrawRectangle(s_backbuffer_width, s_backbuffer_height, false, &dst_rect); + + const int Width = dst_rect.right - dst_rect.left; + const int Height = dst_rect.bottom - dst_rect.top; + + if (xfbSource) + { + const LPDIRECT3DTEXTURE9 tex = ((XFBSource*)xfbSource)->texture; + + // TODO: + + //D3D::drawShadedTexSubQuad(tex, &sourceRc, xfbSource->texWidth, + // xfbSource->texHeight, &drawRc, Width, Height, PixelShaderCache::GetColorCopyProgram(0), + // VertexShaderCache::GetSimpleVertexShader(0)); + } + else + { + const LPDIRECT3DTEXTURE9 tex = FramebufferManager::GetEFBColorTexture(rc); + + D3D::drawShadedTexQuad(tex, sourceRc.AsRECT(), Renderer::GetFullTargetWidth(), + Renderer::GetFullTargetHeight(), Width, Height, + PixelShaderCache::GetColorCopyProgram(g_Config.iMultisampleMode), + VertexShaderCache::GetSimpleVertexShader(g_Config.iMultisampleMode)); + } + + // TODO: good here? + D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER); + D3D::RefreshSamplerState(0, D3DSAMP_MAGFILTER); + + // TODO: this is for overlay text i think, move it elsewhere + D3DVIEWPORT9 vp; + vp.X = 0; + vp.Y = 0; + vp.Width = s_backbuffer_width; + vp.Height = s_backbuffer_height; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + D3D::dev->SetViewport(&vp); +} + +void Renderer::EndFrame() +{ + D3D::EndFrame(); +} + +void Renderer::Present() +{ + D3D::Present(); +} + +void Renderer::GetBackBufferSize(int* w, int* h) +{ + *w = D3D::GetBackBufferWidth(); + *h = D3D::GetBackBufferHeight(); +} + +void Renderer::RecreateFramebufferManger() +{ + // TODO: these ok here? + D3D::dev->SetRenderTarget(0, D3D::GetBackBufferSurface()); + D3D::dev->SetDepthStencilSurface(D3D::GetBackBufferDepthSurface()); + SetupDeviceObjects(); + + delete g_framebuffer_manager; + g_framebuffer_manager = new FramebufferManager; + + D3D::dev->SetRenderTarget(0, FramebufferManager::GetEFBColorRTSurface()); + D3D::dev->SetDepthStencilSurface(FramebufferManager::GetEFBDepthRTSurface()); +} + +void Renderer::BeginFrame() +{ + D3D::BeginFrame(); + D3D::dev->SetRenderTarget(0, FramebufferManager::GetEFBColorRTSurface()); + D3D::dev->SetDepthStencilSurface(FramebufferManager::GetEFBDepthRTSurface()); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_Render.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_Render.h new file mode 100644 index 0000000000..2d63e66fce --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_Render.h @@ -0,0 +1,58 @@ + +#pragma once + +#include "MathUtil.h" + +#include "VideoCommon.h" +#include "Renderer.h" +#include "pluginspecs_video.h" + +namespace DX9 +{ + +class Renderer : public ::RendererBase +{ +public: + Renderer(); + ~Renderer(); + + void ResetAPIState(); + void RestoreAPIState(); + + static void SetupDeviceObjects(); + static void TeardownDeviceObjects(); + + void SetColorMask(); + void SetBlendMode(bool forceUpdate); + bool SetScissorRect(); + + void SetGenerationMode(); + void SetDepthMode(); + void SetLogicOpMode(); + void SetSamplerState(int stage,int texindex); + void SetDitherMode(); + void SetLineWidth(); + + u32 AccessEFB(EFBAccessType type, int x, int y); + + void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z); + void UpdateViewport(); + + // virtual funcs used by RendererBase::Swap + void PrepareXFBCopy(const TargetRectangle &dst_rect); + void Draw(const XFBSourceBase* xfbSource, const TargetRectangle& sourceRc, + const MathUtil::Rectangle& drawRc, const EFBRectangle& rc); + void EndFrame(); + void Present(); + bool CheckForResize(); + void GetBackBufferSize(int* w, int* h); + void RecreateFramebufferManger(); + void BeginFrame(); + + void Swap(u32, FieldType, u32, u32, const EFBRectangle&); + +private: + static bool IS_AMD; +}; + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureCache.cpp new file mode 100644 index 0000000000..907aad6445 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureCache.cpp @@ -0,0 +1,207 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Statistics.h" +#include "MemoryUtil.h" +#include "Hash.h" + +#include "CommonPaths.h" +#include "FileUtil.h" + +#include "DX9_D3DBase.h" +#include "DX9_D3DTexture.h" +#include "DX9_D3DUtil.h" +#include "DX9_FramebufferManager.h" +#include "DX9_PixelShaderCache.h" +#include "PixelShaderManager.h" +#include "VertexShaderManager.h" +#include "DX9_VertexShaderCache.h" +#include "VideoConfig.h" + +#include "DX9_Render.h" + +#include "TextureDecoder.h" +#include "DX9_TextureCache.h" +#include "HiresTextures.h" +#include "DX9_TextureConverter.h" + +#include "../Main.h" + +//#include "debugger/debugger.h" + +namespace DX9 +{ + +TextureCache::TCacheEntry::TCacheEntry(LPDIRECT3DTEXTURE9 _texture) + : texture(_texture), isDynamic(false) +{ + +} + +TextureCache::TCacheEntry::~TCacheEntry() +{ + texture->Release(); +} + +void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level) +{ + D3D::ReplaceTexture2D(texture, temp, width, height, expanded_width, d3d_fmt, swap_r_b, level); +} + +void TextureCache::TCacheEntry::FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect) +{ + LPDIRECT3DSURFACE9 Rendersurf = NULL; + texture->GetSurfaceLevel(0, &Rendersurf); + D3D::dev->SetDepthStencilSurface(NULL); + D3D::dev->SetRenderTarget(0, Rendersurf); + + D3DVIEWPORT9 vp; + + // Stretch picture with increased internal resolution + vp.X = 0; + vp.Y = 0; + vp.Width = Scaledw; + vp.Height = Scaledh; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + D3D::dev->SetViewport(&vp); + RECT destrect; + destrect.bottom = Scaledh; + destrect.left = 0; + destrect.right = Scaledw; + destrect.top = 0; + + const float* const fConstAdd = colmat + 16; // fConstAdd is the last 4 floats of colmat + PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation + TargetRectangle targetSource = Renderer::ConvertEFBRectangle(source_rect); + RECT sourcerect; + sourcerect.bottom = targetSource.bottom; + sourcerect.left = targetSource.left; + sourcerect.right = targetSource.right; + sourcerect.top = targetSource.top; + + if (bFromZBuffer) + { + if (bScaleByHalf || g_ActiveConfig.iMultisampleMode) + { + D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + } + else + { + D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + } + } + else + { + D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + } + + const LPDIRECT3DTEXTURE9 read_texture = bFromZBuffer ? + FramebufferManager::GetEFBDepthTexture(source_rect) : + FramebufferManager::GetEFBColorTexture(source_rect); + + D3DFORMAT bformat = FramebufferManager::GetEFBDepthRTSurfaceFormat(); + int SSAAMode = g_ActiveConfig.iMultisampleMode; + + D3D::drawShadedTexQuad(read_texture, &sourcerect, + Renderer::GetFullTargetWidth(), Renderer::GetFullTargetHeight(), + Scaledw, Scaledh, + ((bformat != FOURCC_RAWZ && bformat != D3DFMT_D24X8) && bFromZBuffer) ? + PixelShaderCache::GetDepthMatrixProgram(SSAAMode) : + PixelShaderCache::GetColorMatrixProgram(SSAAMode), + VertexShaderCache::GetSimpleVertexShader(SSAAMode)); + + Rendersurf->Release(); + + D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER); + D3D::RefreshSamplerState(0, D3DSAMP_MAGFILTER); + D3D::SetTexture(0,NULL); + D3D::dev->SetRenderTarget(0, FramebufferManager::GetEFBColorRTSurface()); + D3D::dev->SetDepthStencilSurface(FramebufferManager::GetEFBDepthRTSurface()); +} + +void TextureCache::TCacheEntry::Bind(unsigned int stage) +{ + D3D::SetTexture(stage, texture); +} + +TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) +{ + D3DFORMAT d3d_fmt; + bool swap_r_b = false; + + switch (pcfmt) + { + case PC_TEX_FMT_BGRA32: + d3d_fmt = D3DFMT_A8R8G8B8; + break; + + case PC_TEX_FMT_RGBA32: + d3d_fmt = D3DFMT_A8R8G8B8; + swap_r_b = true; + break; + + case PC_TEX_FMT_RGB565: + d3d_fmt = D3DFMT_R5G6B5; + break; + + case PC_TEX_FMT_IA4_AS_IA8: + d3d_fmt = D3DFMT_A8L8; + break; + + case PC_TEX_FMT_I8: + case PC_TEX_FMT_I4_AS_I8: + // A hack which means the format is a packed + // 8-bit intensity texture. It is unpacked + // to A8L8 in D3DTexture.cpp + d3d_fmt = D3DFMT_A8P8; + break; + + case PC_TEX_FMT_IA8: + d3d_fmt = D3DFMT_A8L8; + break; + + case PC_TEX_FMT_DXT1: + d3d_fmt = D3DFMT_DXT1; + break; + } + + TCacheEntry* entry = new TCacheEntry(D3D::CreateTexture2D(temp, width, height, expanded_width, d3d_fmt, swap_r_b, tex_levels)); + entry->swap_r_b = swap_r_b; + entry->d3d_fmt = d3d_fmt; + + return entry; +} + +TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(int scaled_tex_w, int scaled_tex_h) +{ + LPDIRECT3DTEXTURE9 texture; + D3D::dev->CreateTexture(scaled_tex_w, scaled_tex_h, 1, D3DUSAGE_RENDERTARGET, + D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture, 0); + + return new TCacheEntry(texture); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureCache.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureCache.h new file mode 100644 index 0000000000..ba35502816 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureCache.h @@ -0,0 +1,67 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _TEXTURECACHE_H +#define _TEXTURECACHE_H + + +#include + +#include "DX9_D3DBase.h" +#include "VideoCommon.h" +#include "BPMemory.h" + +#include "../TextureCache.h" + +namespace DX9 +{ + +class TextureCache : public TextureCacheBase +{ +public: + struct TCacheEntry : TCacheEntryBase + { + LPDIRECT3DTEXTURE9 texture; + + D3DFORMAT d3d_fmt; + bool swap_r_b; + bool isDynamic; // mofified from cpu + + TCacheEntry(LPDIRECT3DTEXTURE9 _texture); + ~TCacheEntry(); + + void Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int levels); + + void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect); + + void Bind(unsigned int stage); + }; + +public: + + TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt); + + TCacheEntryBase* CreateRenderTargetTexture(int scaled_tex_w, int scaled_tex_h); + +}; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureConverter.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureConverter.cpp new file mode 100644 index 0000000000..b38aefe73b --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureConverter.cpp @@ -0,0 +1,560 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Fast image conversion using OpenGL shaders. +// This kind of stuff would be a LOT nicer with OpenCL. + +#include "DX9_TextureConverter.h" +#include "TextureConversionShader.h" +#include "DX9_PixelShaderCache.h" +#include "VertexShaderManager.h" +#include "DX9_VertexShaderCache.h" +#include "DX9_FramebufferManager.h" +#include "VideoConfig.h" +#include "ImageWrite.h" +#include "DX9_Render.h" +#include "DX9_TextureCache.h" +#include "Math.h" +#include "FileUtil.h" + +#include "../Main.h" + +namespace DX9 +{ + +namespace TextureConverter +{ + +struct TransformBuffer +{ + LPDIRECT3DTEXTURE9 FBTexture; + LPDIRECT3DSURFACE9 RenderSurface; + LPDIRECT3DSURFACE9 ReadSurface; + int Width; + int Height; +}; +const u32 NUM_TRANSFORM_BUFFERS = 16; +static TransformBuffer TrnBuffers[NUM_TRANSFORM_BUFFERS]; +static u32 WorkingBuffers = 0; + +static LPDIRECT3DPIXELSHADER9 s_rgbToYuyvProgram = NULL; +static LPDIRECT3DPIXELSHADER9 s_yuyvToRgbProgram = NULL; + +// Not all slots are taken - but who cares. +const u32 NUM_ENCODING_PROGRAMS = 64; +static LPDIRECT3DPIXELSHADER9 s_encodingPrograms[NUM_ENCODING_PROGRAMS]; + +void CreateRgbToYuyvProgram() +{ + // Output is BGRA because that is slightly faster than RGBA. + char* FProgram = new char[2048]; + sprintf(FProgram,"uniform float4 blkDims : register(c%d);\n" + "uniform float4 textureDims : register(c%d);\n" + "uniform sampler samp0 : register(s0);\n" + "void main(\n" + " out float4 ocol0 : COLOR0,\n" + " in float2 uv0 : TEXCOORD0)\n" + "{\n" + " float2 uv1 = float2((uv0.x + 1.0f)/ blkDims.z, uv0.y / blkDims.w);\n" + " float3 c0 = tex2D(samp0, uv0.xy / blkDims.zw).rgb;\n" + " float3 c1 = tex2D(samp0, uv1).rgb;\n" + " float3 y_const = float3(0.257f,0.504f,0.098f);\n" + " float3 u_const = float3(-0.148f,-0.291f,0.439f);\n" + " float3 v_const = float3(0.439f,-0.368f,-0.071f);\n" + " float4 const3 = float4(0.0625f,0.5f,0.0625f,0.5f);\n" + " float3 c01 = (c0 + c1) * 0.5f;\n" + " ocol0 = float4(dot(c1,y_const),dot(c01,u_const),dot(c0,y_const),dot(c01, v_const)) + const3;\n" + "}\n",C_COLORMATRIX,C_COLORMATRIX+1); + + s_rgbToYuyvProgram = D3D::CompileAndCreatePixelShader(FProgram, (int)strlen(FProgram)); + if (!s_rgbToYuyvProgram) { + ERROR_LOG(VIDEO, "Failed to create RGB to YUYV fragment program"); + } + delete [] FProgram; +} + +void CreateYuyvToRgbProgram() +{ + char* FProgram = new char[2048]; + sprintf(FProgram,"uniform float4 blkDims : register(c%d);\n" + "uniform float4 textureDims : register(c%d);\n" + "uniform sampler samp0 : register(s0);\n" + "void main(\n" + " out float4 ocol0 : COLOR0,\n" + " in float2 uv0 : TEXCOORD0)\n" + "{\n" + " float4 c0 = tex2D(samp0, uv0 / blkDims.zw).rgba;\n" + " float f = step(0.5, frac(uv0.x));\n" + " float y = lerp(c0.b, c0.r, f);\n" + " float yComp = 1.164f * (y - 0.0625f);\n" + " float uComp = c0.g - 0.5f;\n" + " float vComp = c0.a - 0.5f;\n" + + " ocol0 = float4(yComp + (1.596f * vComp),\n" + " yComp - (0.813f * vComp) - (0.391f * uComp),\n" + " yComp + (2.018f * uComp),\n" + " 1.0f);\n" + "}\n",C_COLORMATRIX,C_COLORMATRIX+1); + s_yuyvToRgbProgram = D3D::CompileAndCreatePixelShader(FProgram, (int)strlen(FProgram)); + if (!s_yuyvToRgbProgram) { + ERROR_LOG(VIDEO, "Failed to create YUYV to RGB fragment program"); + } + delete [] FProgram; +} + +LPDIRECT3DPIXELSHADER9 GetOrCreateEncodingShader(u32 format) +{ + if (format > NUM_ENCODING_PROGRAMS) + { + PanicAlert("Unknown texture copy format: 0x%x\n", format); + return s_encodingPrograms[0]; + } + + if (!s_encodingPrograms[format]) + { + const char* shader = TextureConversionShader::GenerateEncodingShader(format,API_D3D9); + +#if defined(_DEBUG) || defined(DEBUGFAST) + if (g_ActiveConfig.iLog & CONF_SAVESHADERS && shader) { + static int counter = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%senc_%04i.txt", File::GetUserPath(D_DUMP_IDX), counter++); + + SaveData(szTemp, shader); + } +#endif + s_encodingPrograms[format] = D3D::CompileAndCreatePixelShader(shader, (int)strlen(shader)); + if (!s_encodingPrograms[format]) { + ERROR_LOG(VIDEO, "Failed to create encoding fragment program"); + } + } + return s_encodingPrograms[format]; +} + +void Init() +{ + for (unsigned int i = 0; i < NUM_ENCODING_PROGRAMS; i++) + { + s_encodingPrograms[i] = NULL; + } + for (unsigned int i = 0; i < NUM_TRANSFORM_BUFFERS; i++) + { + TrnBuffers[i].FBTexture = NULL; + TrnBuffers[i].RenderSurface = NULL; + TrnBuffers[i].ReadSurface = NULL; + TrnBuffers[i].Width = 0; + TrnBuffers[i].Height = 0; + } + CreateRgbToYuyvProgram(); + CreateYuyvToRgbProgram(); + +} + +void Shutdown() +{ + if(s_rgbToYuyvProgram) + s_rgbToYuyvProgram->Release(); + s_rgbToYuyvProgram = NULL; + if(s_yuyvToRgbProgram) + s_yuyvToRgbProgram->Release(); + s_yuyvToRgbProgram=NULL; + + for (unsigned int i = 0; i < NUM_ENCODING_PROGRAMS; i++) + { + if(s_encodingPrograms[i]) + s_encodingPrograms[i]->Release(); + s_encodingPrograms[i] = NULL; + } + for (unsigned int i = 0; i < NUM_TRANSFORM_BUFFERS; i++) + { + if(TrnBuffers[i].RenderSurface != NULL) + TrnBuffers[i].RenderSurface->Release(); + TrnBuffers[i].RenderSurface = NULL; + + if(TrnBuffers[i].ReadSurface != NULL) + TrnBuffers[i].ReadSurface->Release(); + TrnBuffers[i].ReadSurface = NULL; + + if(TrnBuffers[i].FBTexture != NULL) + TrnBuffers[i].FBTexture->Release(); + TrnBuffers[i].FBTexture = NULL; + + TrnBuffers[i].Width = 0; + TrnBuffers[i].Height = 0; + } + WorkingBuffers = 0; +} + +void EncodeToRamUsingShader(LPDIRECT3DPIXELSHADER9 shader, LPDIRECT3DTEXTURE9 srcTexture, const TargetRectangle& sourceRc, + u8* destAddr, int dstWidth, int dstHeight, int readStride, bool toTexture, bool linearFilter) +{ + HRESULT hr; + u32 index =0; + while(index < WorkingBuffers && (TrnBuffers[index].Width != dstWidth || TrnBuffers[index].Height != dstHeight)) + index++; + + LPDIRECT3DSURFACE9 s_texConvReadSurface = NULL; + LPDIRECT3DSURFACE9 Rendersurf = NULL; + + if (index >= WorkingBuffers) + { + if (WorkingBuffers < NUM_TRANSFORM_BUFFERS) + WorkingBuffers++; + if (index >= WorkingBuffers) + index--; + if (TrnBuffers[index].RenderSurface != NULL) + { + TrnBuffers[index].RenderSurface->Release(); + TrnBuffers[index].RenderSurface = NULL; + } + if (TrnBuffers[index].ReadSurface != NULL) + { + TrnBuffers[index].ReadSurface->Release(); + TrnBuffers[index].ReadSurface = NULL; + } + if (TrnBuffers[index].FBTexture != NULL) + { + TrnBuffers[index].FBTexture->Release(); + TrnBuffers[index].FBTexture = NULL; + } + TrnBuffers[index].Width = dstWidth; + TrnBuffers[index].Height = dstHeight; + D3D::dev->CreateTexture(dstWidth, dstHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, &TrnBuffers[index].FBTexture, NULL); + TrnBuffers[index].FBTexture->GetSurfaceLevel(0,&TrnBuffers[index].RenderSurface); + D3D::dev->CreateOffscreenPlainSurface(dstWidth, dstHeight, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &TrnBuffers[index].ReadSurface, NULL ); + } + + s_texConvReadSurface = TrnBuffers[index].ReadSurface; + Rendersurf = TrnBuffers[index].RenderSurface; + + hr = D3D::dev->SetDepthStencilSurface(NULL); + hr = D3D::dev->SetRenderTarget(0, Rendersurf); + + if (linearFilter) + { + D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + } + else + { + D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + } + + D3DVIEWPORT9 vp; + vp.X = 0; + vp.Y = 0; + vp.Width = dstWidth; + vp.Height = dstHeight; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + hr = D3D::dev->SetViewport(&vp); + RECT SrcRect; + SrcRect.top = sourceRc.top; + SrcRect.left = sourceRc.left; + SrcRect.right = sourceRc.right; + SrcRect.bottom = sourceRc.bottom; + RECT DstRect; + DstRect.top = 0; + DstRect.left = 0; + DstRect.right = dstWidth; + DstRect.bottom = dstHeight; + + + // Draw... + D3D::drawShadedTexQuad(srcTexture,&SrcRect,1,1,dstWidth,dstHeight,shader,VertexShaderCache::GetSimpleVertexShader(0)); + D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER); + // .. and then readback the results. + // TODO: make this less slow. + + D3DLOCKED_RECT drect; + + + hr = D3D::dev->GetRenderTargetData(Rendersurf,s_texConvReadSurface); + if((hr = s_texConvReadSurface->LockRect(&drect, &DstRect, D3DLOCK_READONLY)) != D3D_OK) + { + PanicAlert("ERROR: %s", hr == D3DERR_WASSTILLDRAWING ? "Still drawing" : + hr == D3DERR_INVALIDCALL ? "Invalid call" : "w00t"); + + } + else + { + int writeStride = bpmem.copyMipMapStrideChannels * 32; + + if (writeStride != readStride && toTexture) + { + // writing to a texture of a different size + + int readHeight = readStride / dstWidth; + + int readStart = 0; + int readLoops = dstHeight / (readHeight/4); // 4 bytes per pixel + u8 *Source = (u8*)drect.pBits; + for (int i = 0; i < readLoops; i++) + { + int readDist = dstWidth*readHeight; + memcpy(destAddr,Source,readDist); + Source += readDist; + destAddr += writeStride; + } + } + else + memcpy(destAddr,drect.pBits,dstWidth*dstHeight*4);// 4 bytes per pixel + + hr = s_texConvReadSurface->UnlockRect(); + } +} + +void EncodeToRam(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) +{ + u32 format = copyfmt; + + if (bFromZBuffer) + { + format |= _GX_TF_ZTF; + if (copyfmt == 11) + format = GX_TF_Z16; + else if (format < GX_TF_Z8 || format > GX_TF_Z24X8) + format |= _GX_TF_CTF; + } + else + if (copyfmt > GX_TF_RGBA8 || (copyfmt < GX_TF_RGB565 && !bIsIntensityFmt)) + format |= _GX_TF_CTF; + + LPDIRECT3DPIXELSHADER9 texconv_shader = GetOrCreateEncodingShader(format); + if (!texconv_shader) + return; + + u8 *dest_ptr = Memory_GetPtr(address); + + LPDIRECT3DTEXTURE9 source_texture = bFromZBuffer ? + FramebufferManager::GetEFBDepthTexture(source) : + FramebufferManager::GetEFBColorTexture(source); + int width = (source.right - source.left) >> bScaleByHalf; + int height = (source.bottom - source.top) >> bScaleByHalf; + + int size_in_bytes = TexDecoder_GetTextureSizeInBytes(width, height, format); + + // Invalidate any existing texture covering this memory range. + // TODO - don't delete the texture if it already exists, just replace the contents. + TextureCache::InvalidateRange(address, size_in_bytes); + + u16 blkW = TexDecoder_GetBlockWidthInTexels(format) - 1; + u16 blkH = TexDecoder_GetBlockHeightInTexels(format) - 1; + u16 samples = TextureConversionShader::GetEncodedSampleCount(format); + + // only copy on cache line boundaries + // extra pixels are copied but not displayed in the resulting texture + s32 expandedWidth = (width + blkW) & (~blkW); + s32 expandedHeight = (height + blkH) & (~blkH); + + float MValueX = Renderer::GetTargetScaleX(); + float MValueY = Renderer::GetTargetScaleY(); + + float Xstride = (float)((Renderer::GetFullTargetWidth() - Renderer::GetTargetWidth()) / 2); + float Ystride = (float)((Renderer::GetFullTargetHeight() - Renderer::GetTargetHeight()) / 2); + + float sampleStride = bScaleByHalf?2.0f:1.0f; + + TextureConversionShader::SetShaderParameters( + (float)expandedWidth, + expandedHeight * MValueY, + source.left * MValueX + Xstride , + source.top * MValueY + Ystride, + sampleStride * MValueX, + sampleStride * MValueY, + (float)Renderer::GetFullTargetWidth(), + (float)Renderer::GetFullTargetHeight()); + + TargetRectangle scaledSource; + scaledSource.top = 0; + scaledSource.bottom = expandedHeight; + scaledSource.left = 0; + scaledSource.right = expandedWidth / samples; + int cacheBytes = 32; + if ((format & 0x0f) == 6) + cacheBytes = 64; + + int readStride = (expandedWidth * cacheBytes) / TexDecoder_GetBlockWidthInTexels(format); + g_renderer->ResetAPIState(); + EncodeToRamUsingShader(texconv_shader, source_texture, scaledSource, dest_ptr, expandedWidth / samples, expandedHeight,readStride, true, bScaleByHalf > 0); + D3D::dev->SetRenderTarget(0, FramebufferManager::GetEFBColorRTSurface()); + D3D::dev->SetDepthStencilSurface(FramebufferManager::GetEFBDepthRTSurface()); + g_renderer->RestoreAPIState(); +} + +u64 EncodeToRamFromTexture(u32 address,LPDIRECT3DTEXTURE9 source_texture,u32 SourceW, u32 SourceH,float MValueX,float MValueY,float Xstride, float Ystride , bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) +{ + u32 format = copyfmt; + + if (bFromZBuffer) + { + format |= _GX_TF_ZTF; + if (copyfmt == 11) + format = GX_TF_Z16; + else if (format < GX_TF_Z8 || format > GX_TF_Z24X8) + format |= _GX_TF_CTF; + } + else + if (copyfmt > GX_TF_RGBA8 || (copyfmt < GX_TF_RGB565 && !bIsIntensityFmt)) + format |= _GX_TF_CTF; + + LPDIRECT3DPIXELSHADER9 texconv_shader = GetOrCreateEncodingShader(format); + if (!texconv_shader) + return 0; + + u8 *dest_ptr = Memory_GetPtr(address); + + int width = (source.right - source.left) >> bScaleByHalf; + int height = (source.bottom - source.top) >> bScaleByHalf; + + int size_in_bytes = TexDecoder_GetTextureSizeInBytes(width, height, format); + + u16 blkW = TexDecoder_GetBlockWidthInTexels(format) - 1; + u16 blkH = TexDecoder_GetBlockHeightInTexels(format) - 1; + u16 samples = TextureConversionShader::GetEncodedSampleCount(format); + + // only copy on cache line boundaries + // extra pixels are copied but not displayed in the resulting texture + s32 expandedWidth = (width + blkW) & (~blkW); + s32 expandedHeight = (height + blkH) & (~blkH); + + float sampleStride = bScaleByHalf?2.0f:1.0f; + + TextureConversionShader::SetShaderParameters( + (float)expandedWidth, + expandedHeight * MValueY, + source.left * MValueX + Xstride , + source.top * MValueY + Ystride, + sampleStride * MValueX, + sampleStride * MValueY, + (float)SourceW, + (float)SourceH); + + TargetRectangle scaledSource; + scaledSource.top = 0; + scaledSource.bottom = expandedHeight; + scaledSource.left = 0; + scaledSource.right = expandedWidth / samples; + int cacheBytes = 32; + if ((format & 0x0f) == 6) + cacheBytes = 64; + + int readStride = (expandedWidth * cacheBytes) / TexDecoder_GetBlockWidthInTexels(format); + EncodeToRamUsingShader(texconv_shader, source_texture, scaledSource, dest_ptr, expandedWidth / samples, expandedHeight,readStride, true, bScaleByHalf > 0); + TextureCache::MakeRangeDynamic(address,size_in_bytes); + u64 Hashvalue = 0; + Hashvalue = GetHash64(dest_ptr,size_in_bytes,g_ActiveConfig.iSafeTextureCache_ColorSamples); + return Hashvalue; +} + + +void EncodeToRamYUYV(LPDIRECT3DTEXTURE9 srcTexture, const TargetRectangle& sourceRc,u8* destAddr, int dstWidth, int dstHeight) +{ + TextureConversionShader::SetShaderParameters( + (float)dstWidth, + (float)dstHeight, + 0.0f , + 0.0f, + 1.0f, + 1.0f, + (float)Renderer::GetFullTargetWidth(), + (float)Renderer::GetFullTargetHeight()); + g_renderer->ResetAPIState(); + EncodeToRamUsingShader(s_rgbToYuyvProgram, srcTexture, sourceRc, destAddr, dstWidth / 2, dstHeight, 0, false, false); + D3D::dev->SetRenderTarget(0, FramebufferManager::GetEFBColorRTSurface()); + D3D::dev->SetDepthStencilSurface(FramebufferManager::GetEFBDepthRTSurface()); + g_renderer->RestoreAPIState(); +} + + +// Should be scale free. +void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, LPDIRECT3DTEXTURE9 destTexture) +{ + u8* srcAddr = Memory_GetPtr(xfbAddr); + if (!srcAddr) + { + WARN_LOG(VIDEO, "Tried to decode from invalid memory address"); + return; + } + + int srcFmtWidth = srcWidth / 2; + + g_renderer->ResetAPIState(); // reset any game specific settings + LPDIRECT3DTEXTURE9 s_srcTexture = D3D::CreateTexture2D(srcAddr, srcFmtWidth, srcHeight, srcFmtWidth, D3DFMT_A8R8G8B8, false); + LPDIRECT3DSURFACE9 Rendersurf = NULL; + destTexture->GetSurfaceLevel(0,&Rendersurf); + D3D::dev->SetDepthStencilSurface(NULL); + D3D::dev->SetRenderTarget(0, Rendersurf); + + D3DVIEWPORT9 vp; + + // Stretch picture with increased internal resolution + vp.X = 0; + vp.Y = 0; + vp.Width = srcWidth; + vp.Height = srcHeight; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + D3D::dev->SetViewport(&vp); + RECT destrect; + destrect.bottom = srcHeight; + destrect.left = 0; + destrect.right = srcWidth; + destrect.top = 0; + + RECT sourcerect; + sourcerect.bottom = srcHeight; + sourcerect.left = 0; + sourcerect.right = srcFmtWidth; + sourcerect.top = 0; + + D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + + TextureConversionShader::SetShaderParameters( + (float)srcFmtWidth, + (float)srcHeight, + 0.0f , + 0.0f, + 1.0f, + 1.0f, + (float)srcFmtWidth, + (float)srcHeight); + D3D::drawShadedTexQuad( + s_srcTexture, + &sourcerect, + 1 , + 1, + srcWidth, + srcHeight, + s_yuyvToRgbProgram, + VertexShaderCache::GetSimpleVertexShader(0)); + + + D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER); + D3D::RefreshSamplerState(0, D3DSAMP_MAGFILTER); + D3D::SetTexture(0,NULL); + D3D::dev->SetRenderTarget(0, FramebufferManager::GetEFBColorRTSurface()); + D3D::dev->SetDepthStencilSurface(FramebufferManager::GetEFBDepthRTSurface()); + g_renderer->RestoreAPIState(); + Rendersurf->Release(); + s_srcTexture->Release(); +} + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureConverter.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureConverter.h new file mode 100644 index 0000000000..bff2e88fd3 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_TextureConverter.h @@ -0,0 +1,53 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _TEXTURECONVERTER_H_ +#define _TEXTURECONVERTER_H_ + +#include "VideoCommon.h" +#include "DX9_D3DBase.h" +#include "DX9_D3DTexture.h" +#include "DX9_D3DUtil.h" +#include "DX9_D3DShader.h" + +namespace DX9 +{ + +// Converts textures between formats +// TODO: support multiple texture formats +namespace TextureConverter +{ + +void Init(); +void Shutdown(); + +void EncodeToRam(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, + u32 copyfmt, int bScaleByHalf, const EFBRectangle& source); + +void EncodeToRamYUYV(LPDIRECT3DTEXTURE9 srcTexture, const TargetRectangle& sourceRc, + u8* destAddr, int dstWidth, int dstHeight); + +void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, LPDIRECT3DTEXTURE9 destTexture); + +u64 EncodeToRamFromTexture(u32 address,LPDIRECT3DTEXTURE9 source_texture,u32 SourceW, u32 SourceH,float MValueX,float MValueY,float Xstride, float Ystride , bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source); + + +} + +} + +#endif // _TEXTURECONVERTER_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexManager.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexManager.cpp new file mode 100644 index 0000000000..0669667350 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexManager.cpp @@ -0,0 +1,135 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "FileUtil.h" + +#include "DX9_D3DBase.h" +#include "Fifo.h" +#include "Statistics.h" +#include "Profiler.h" +#include "DX9_VertexManager.h" +#include "OpcodeDecoding.h" +#include "IndexGenerator.h" +#include "VertexShaderManager.h" +#include "DX9_VertexShaderCache.h" +#include "PixelShaderManager.h" +#include "DX9_PixelShaderCache.h" +#include "NativeVertexFormat.h" +#include "NativeVertexWriter.h" +#include "DX9_TextureCache.h" + +#include "../Main.h" + +#include "BPStructs.h" +#include "XFStructs.h" + +//#include "debugger/debugger.h" + +namespace DX9 +{ + +using namespace D3D; + +// internal state for loading vertices +extern NativeVertexFormat *g_nativeVertexFmt; + +inline void DumpBadShaders() +{ +#if defined(_DEBUG) || defined(DEBUGFAST) + std::string error_shaders; + error_shaders.append(VertexShaderCache::GetCurrentShaderCode()); + error_shaders.append(PixelShaderCache::GetCurrentShaderCode()); + char filename[512] = "bad_shader_combo_0.txt"; + int which = 0; + while (File::Exists(filename)) + { + which++; + sprintf(filename, "bad_shader_combo_%i.txt", which); + } + File::WriteStringToFile(true, error_shaders, filename); + PanicAlert("DrawIndexedPrimitiveUP failed. Shaders written to %s", filename); +#endif +} + +void VertexManager::Draw(u32 stride, bool alphapass) +{ + if (alphapass) + { + DWORD write = 0; + if (false == g_pixel_shader_cache->SetShader(true)) + { + //DEBUGGER_PAUSE_LOG_AT(NEXT_ERROR,true,{printf("Fail to set pixel shader\n");}); + //goto shader_fail; + return; + } + // update alpha only + D3D::ChangeRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA); + D3D::ChangeRenderState(D3DRS_ALPHABLENDENABLE, false); + } + + if (IndexGenerator::GetNumTriangles() > 0) + { + if (FAILED(D3D::dev->DrawIndexedPrimitiveUP( + D3DPT_TRIANGLELIST, + 0, IndexGenerator::GetNumVerts(), IndexGenerator::GetNumTriangles(), + TIBuffer, + D3DFMT_INDEX16, + LocalVBuffer, + stride))) + { + DumpBadShaders(); + } + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + if (IndexGenerator::GetNumLines() > 0) + { + if (FAILED(D3D::dev->DrawIndexedPrimitiveUP( + D3DPT_LINELIST, + 0, IndexGenerator::GetNumVerts(), IndexGenerator::GetNumLines(), + LIBuffer, + D3DFMT_INDEX16, + LocalVBuffer, + stride))) + { + DumpBadShaders(); + } + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + if (IndexGenerator::GetNumPoints() > 0) + { + if (FAILED(D3D::dev->DrawIndexedPrimitiveUP( + D3DPT_POINTLIST, + 0, IndexGenerator::GetNumVerts(), IndexGenerator::GetNumPoints(), + PIBuffer, + D3DFMT_INDEX16, + LocalVBuffer, + stride))) + { + DumpBadShaders(); + } + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + + if (alphapass) + { + D3D::RefreshRenderState(D3DRS_COLORWRITEENABLE); + D3D::RefreshRenderState(D3DRS_ALPHABLENDENABLE); + } +} + +} // namespace diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexManager.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexManager.h new file mode 100644 index 0000000000..e0c296f7c9 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexManager.h @@ -0,0 +1,39 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _VERTEXMANAGER_H +#define _VERTEXMANAGER_H + +#include "CPMemory.h" +#include "VertexLoader.h" + +#include "../VertexManager.h" + +namespace DX9 +{ + +class VertexManager : public ::VertexManagerBase +{ +public: + void Draw(u32 stride, bool alphapass); + + NativeVertexFormat* CreateNativeVertexFormat(); +}; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexShaderCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexShaderCache.cpp new file mode 100644 index 0000000000..86261d9b9c --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexShaderCache.cpp @@ -0,0 +1,302 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "FileUtil.h" +#include "LinearDiskCache.h" + +#include "DX9_D3DBase.h" +#include "DX9_D3DShader.h" +#include "Statistics.h" +#include "Profiler.h" +#include "VideoConfig.h" +#include "DX9_VertexShaderCache.h" +#include "VertexLoader.h" +#include "BPMemory.h" +#include "XFMemory.h" + +//#include "debugger/debugger.h" + +#include "../Main.h" + +namespace DX9 +{ + +VertexShaderCache::VSCache VertexShaderCache::vshaders; +const VertexShaderCache::VSCacheEntry *VertexShaderCache::last_entry; + +#define MAX_SSAA_SHADERS 3 + +static LPDIRECT3DVERTEXSHADER9 SimpleVertexShader[MAX_SSAA_SHADERS]; +static LPDIRECT3DVERTEXSHADER9 ClearVertexShader; + +LinearDiskCache g_vs_disk_cache; + +LPDIRECT3DVERTEXSHADER9 VertexShaderCache::GetSimpleVertexShader(int level) +{ + return SimpleVertexShader[level % MAX_SSAA_SHADERS]; +} + +LPDIRECT3DVERTEXSHADER9 VertexShaderCache::GetClearVertexShader() +{ + return ClearVertexShader; +} + + +void VertexShaderCache::SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + const float f[4] = { f1, f2, f3, f4 }; + D3D::dev->SetVertexShaderConstantF(const_number, f, 1); +} + +void VertexShaderCache::SetVSConstant4fv(unsigned int const_number, const float *f) +{ + D3D::dev->SetVertexShaderConstantF(const_number, f, 1); +} + +void VertexShaderCache::SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float *f) +{ + float buf[4*C_VENVCONST_END]; + for (unsigned int i = 0; i < count; i++) + { + buf[4*i ] = *f++; + buf[4*i+1] = *f++; + buf[4*i+2] = *f++; + buf[4*i+3] = 0.f; + } + D3D::dev->SetVertexShaderConstantF(const_number, buf, count); +} + +void VertexShaderCache::SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float *f) +{ + D3D::dev->SetVertexShaderConstantF(const_number, f, count); +} + +class VertexShaderCacheInserter : public LinearDiskCacheReader { +public: + void Read(const u8 *key, int key_size, const u8 *value, int value_size) + { + VERTEXSHADERUID uid; + if (key_size != sizeof(uid)) { + ERROR_LOG(VIDEO, "Wrong key size in vertex shader cache"); + return; + } + memcpy(&uid, key, key_size); + VertexShaderCache::InsertByteCode(uid, value, value_size, false); + } +}; + +VertexShaderCache::VertexShaderCache() +{ + char* vProg = new char[2048]; + sprintf(vProg,"struct VSOUTPUT\n" + "{\n" + "float4 vPosition : POSITION;\n" + "float2 vTexCoord : TEXCOORD0;\n" + "};\n" + "VSOUTPUT main(float4 inPosition : POSITION,float2 inTEX0 : TEXCOORD0)\n" + "{\n" + "VSOUTPUT OUT;\n" + "OUT.vPosition = inPosition;\n" + "OUT.vTexCoord = inTEX0;\n" + "return OUT;\n" + "}\n"); + + SimpleVertexShader[0] = D3D::CompileAndCreateVertexShader(vProg, (int)strlen(vProg)); + + sprintf(vProg,"struct VSOUTPUT\n" + "{\n" + "float4 vPosition : POSITION;\n" + "float4 vColor0 : COLOR0;\n" + "};\n" + "VSOUTPUT main(float4 inPosition : POSITION,float4 inColor0: COLOR0)\n" + "{\n" + "VSOUTPUT OUT;\n" + "OUT.vPosition = inPosition;\n" + "OUT.vColor0 = inColor0;\n" + "return OUT;\n" + "}\n"); + + ClearVertexShader = D3D::CompileAndCreateVertexShader(vProg, (int)strlen(vProg)); + + sprintf(vProg, "struct VSOUTPUT\n" + "{\n" + "float4 vPosition : POSITION;\n" + "float4 vTexCoord : TEXCOORD0;\n" + "float4 vTexCoord1 : TEXCOORD1;\n" + "};\n" + "VSOUTPUT main(float4 inPosition : POSITION,float2 inTEX0 : TEXCOORD0,float2 inTEX1 : TEXCOORD1,float4 inTEX2 : TEXCOORD2)\n" + "{\n" + "VSOUTPUT OUT;" + "OUT.vPosition = inPosition;\n" + "OUT.vTexCoord = inTEX0.xyyx;\n" + "OUT.vTexCoord1 = inTEX2;\n" + "return OUT;\n" + "}\n"); + SimpleVertexShader[1] = D3D::CompileAndCreateVertexShader(vProg, (int)strlen(vProg)); + + sprintf(vProg, "struct VSOUTPUT\n" + "{\n" + "float4 vPosition : POSITION;\n" + "float4 vTexCoord : TEXCOORD0;\n" + "float4 vTexCoord1 : TEXCOORD1;\n" + "float4 vTexCoord2 : TEXCOORD2;\n" + "float4 vTexCoord3 : TEXCOORD3;\n" + "};\n" + "VSOUTPUT main(float4 inPosition : POSITION,float2 inTEX0 : TEXCOORD0,float2 inTEX1 : TEXCOORD1,float4 inTEX2 : TEXCOORD2)\n" + "{\n" + "VSOUTPUT OUT;" + "OUT.vPosition = inPosition;\n" + "OUT.vTexCoord = inTEX0.xyyx;\n" + "OUT.vTexCoord1 = inTEX0.xyyx + (float4(-1.0f,-0.5f, 1.0f,-0.5f) * inTEX1.xyyx);\n" + "OUT.vTexCoord2 = inTEX0.xyyx + (float4( 1.0f, 0.5f,-1.0f, 0.5f) * inTEX1.xyyx);\n" + "OUT.vTexCoord3 = inTEX2;\n" + "return OUT;\n" + "}\n"); + SimpleVertexShader[2] = D3D::CompileAndCreateVertexShader(vProg, (int)strlen(vProg)); + + Clear(); + delete [] vProg; + + if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) + File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); + + SETSTAT(stats.numVertexShadersCreated, 0); + SETSTAT(stats.numVertexShadersAlive, 0); + + char cache_filename[MAX_PATH]; + sprintf(cache_filename, "%sdx9-%s-vs.cache", File::GetUserPath(D_SHADERCACHE_IDX), g_globals->unique_id); + VertexShaderCacheInserter inserter; + int read_items = g_vs_disk_cache.OpenAndRead(cache_filename, &inserter); +} + +void VertexShaderCache::Clear() +{ + VSCache::iterator + iter = vshaders.begin(), + vsend = vshaders.end(); + for (; iter != vsend; ++iter) + iter->second.Destroy(); + + vshaders.clear(); + + memset(&last_vertex_shader_uid, 0xFF, sizeof(last_vertex_shader_uid)); +} + +VertexShaderCache::~VertexShaderCache() +{ + for (int i = 0; i < MAX_SSAA_SHADERS; i++) + { + if (SimpleVertexShader[i]) + SimpleVertexShader[i]->Release(); + SimpleVertexShader[i] = NULL; + } + + if (ClearVertexShader) + ClearVertexShader->Release(); + + ClearVertexShader = NULL; + + g_vs_disk_cache.Sync(); + g_vs_disk_cache.Close(); +} + +bool VertexShaderCache::SetShader(u32 components) +{ + DVSTARTPROFILE(); + + VERTEXSHADERUID uid; + GetVertexShaderId(&uid, components); + if (uid == last_vertex_shader_uid && vshaders[uid].frameCount == frameCount) + { + if (vshaders[uid].shader) + return true; + else + return false; + } + memcpy(&last_vertex_shader_uid, &uid, sizeof(VERTEXSHADERUID)); + + VSCache::iterator iter; + iter = vshaders.find(uid); + if (iter != vshaders.end()) + { + iter->second.frameCount = frameCount; + const VSCacheEntry &entry = iter->second; + last_entry = &entry; + + //DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true); + if (entry.shader) + { + D3D::SetVertexShader(entry.shader); + return true; + } + else + return false; + } + + const char *code = GenerateVertexShaderCode(components, API_D3D9); + u8 *bytecode; + int bytecodelen; + if (!D3D::CompileVertexShader(code, (int)strlen(code), &bytecode, &bytecodelen)) + { + if (g_ActiveConfig.bShowShaderErrors) + { + PanicAlert("Failed to compile Vertex Shader:\n\n%s", code); + } + return false; + } + g_vs_disk_cache.Append((u8 *)&uid, sizeof(uid), bytecode, bytecodelen); + g_vs_disk_cache.Sync(); + + bool result = InsertByteCode(uid, bytecode, bytecodelen, true); + delete [] bytecode; + return result; +} + +bool VertexShaderCache::InsertByteCode(const VERTEXSHADERUID &uid, const u8 *bytecode, int bytecodelen, bool activate) { + LPDIRECT3DVERTEXSHADER9 shader = D3D::CreateVertexShaderFromByteCode(bytecode, bytecodelen); + + // Make an entry in the table + VSCacheEntry entry; + entry.shader = shader; + entry.frameCount = frameCount; + + vshaders[uid] = entry; + last_entry = &vshaders[uid]; + if (!shader) + return false; + + INCSTAT(stats.numVertexShadersCreated); + SETSTAT(stats.numVertexShadersAlive, (int)vshaders.size()); + if (activate) + { + D3D::SetVertexShader(shader); + return true; + } + return false; +} + +#if defined(_DEBUG) || defined(DEBUGFAST) +std::string VertexShaderCache::GetCurrentShaderCode() +{ + return "(N/A)\n"; +} +#endif + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexShaderCache.h b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexShaderCache.h new file mode 100644 index 0000000000..ca22125ae1 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/DX9/DX9_VertexShaderCache.h @@ -0,0 +1,81 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _VERTEXSHADERCACHE_H +#define _VERTEXSHADERCACHE_H + +#include "DX9_D3DBase.h" + +#include +#include + +#include "DX9_D3DBase.h" +#include "VertexShaderGen.h" + +#include "../VertexShaderCache.h" + +namespace DX9 +{ + +class VertexShaderCache : public ::VertexShaderCacheBase +{ +private: + struct VSCacheEntry + { + LPDIRECT3DVERTEXSHADER9 shader; + int frameCount; +#if defined(_DEBUG) || defined(DEBUGFAST) + std::string code; +#endif + VSCacheEntry() : shader(NULL), frameCount(0) {} + void Destroy() + { + if (shader) + shader->Release(); + shader = NULL; + } + }; + + typedef std::map VSCache; + + static VSCache vshaders; + static const VSCacheEntry *last_entry; + static void Clear(); + +public: + VertexShaderCache(); + ~VertexShaderCache(); + + void SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4); + void SetVSConstant4fv(unsigned int const_number, const float *f); + void SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float *f); + void SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float *f); + + bool SetShader(u32 components); + + static LPDIRECT3DVERTEXSHADER9 GetSimpleVertexShader(int level); + static LPDIRECT3DVERTEXSHADER9 GetClearVertexShader(); + static bool InsertByteCode(const VERTEXSHADERUID &uid, const u8 *bytecode, int bytecodelen, bool activate); +#if defined(_DEBUG) || defined(DEBUGFAST) + static std::string GetCurrentShaderCode(); +#endif + static LPDIRECT3DVERTEXSHADER9 CompileCgShader(const char *pstrprogram); +}; + +} + +#endif // _VERTEXSHADERCACHE_H diff --git a/Source/Plugins/Plugin_VideoMerge/Src/EmuWindow.cpp b/Source/Plugins/Plugin_VideoMerge/Src/EmuWindow.cpp new file mode 100644 index 0000000000..0fed997192 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/EmuWindow.cpp @@ -0,0 +1,233 @@ + +#include + +// VideoCommon +#include "VideoConfig.h" +#include "Fifo.h" + +#include "EmuWindow.h" + +int OSDChoice = 0 , OSDTime = 0, OSDInternalW = 0, OSDInternalH = 0; + +namespace EmuWindow +{ + +HWND m_hWnd = NULL; +HWND m_hParent = NULL; +HINSTANCE m_hInstance = NULL; +WNDCLASSEX wndClass; +const TCHAR m_szClassName[] = _T("DolphinEmuWnd"); +int g_winstyle; +static volatile bool s_sizing; + +bool IsSizing() +{ + return s_sizing; +} + +HWND GetWnd() +{ + return m_hWnd; +} + +HWND GetParentWnd() +{ + return m_hParent; +} + +// --------------------------------------------------------------------- +// KeyDown events +// ------------- +void OnKeyDown(WPARAM wParam) +{ + switch (LOWORD( wParam )) + { + case '3': // OSD keys + case '4': + case '5': + case '6': + case '7': + if (g_Config.bOSDHotKey) + OSDMenu(wParam); + break; + } +} +// --------------------------------------------------------------------- + +LRESULT CALLBACK WndProc( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam ) +{ + switch( iMsg ) + { + case WM_PAINT: + { + HDC hdc; + PAINTSTRUCT ps; + hdc = BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + } + break; + + case WM_ENTERSIZEMOVE: + s_sizing = true; + break; + + case WM_EXITSIZEMOVE: + s_sizing = false; + break; + + /* Post thes mouse events to the main window, it's nessesary because in difference to the + keyboard inputs these events only appear here, not in the parent window or any other WndProc()*/ + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + PostMessage(GetParentWnd(), iMsg, wParam, lParam); + break; + + case WM_CLOSE: + // When the user closes the window, we post an event to the main window to call Stop() + // Which then handles all the necessary steps to Shutdown the core + the plugins + if (m_hParent == NULL) + PostMessage(m_hParent, WM_USER, WM_USER_STOP, 0); + break; + + case WM_USER: + if (wParam == WM_USER_KEYDOWN) + OnKeyDown(lParam); + else if (wParam == WIIMOTE_DISCONNECT) + PostMessage(m_hParent, WM_USER, wParam, lParam); + break; + + case WM_SYSCOMMAND: + switch (wParam) + { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + break; + default: + return DefWindowProc(hWnd, iMsg, wParam, lParam); + } + break; + + case WM_SETCURSOR: + PostMessage(m_hParent, WM_USER, WM_USER_SETCURSOR, 0); + return true; + break; + + default: + return DefWindowProc(hWnd, iMsg, wParam, lParam); + } + return 0; +} + +// --------------------------------------------------------------------- +// OSD Menu +// ------------- +// Let's begin with 3 since 1 and 2 are default Wii keys +// ------------- +void OSDMenu(WPARAM wParam) +{ + switch( LOWORD( wParam )) + { + //case '3': + // OSDChoice = 1; + // // Toggle native resolution + // OSDInternalW = D3D::GetBackBufferWidth(); + // OSDInternalH = D3D::GetBackBufferHeight(); + // break; + case '4': + OSDChoice = 2; + // Toggle aspect ratio + g_Config.iAspectRatio = (g_Config.iAspectRatio + 1) & 3; + break; + case '5': + OSDChoice = 3; + PanicAlert("Toggling EFB copy not implemented!\n"); + break; + case '6': + OSDChoice = 4; + g_Config.bDisableFog = !g_Config.bDisableFog; + break; + case '7': + OSDChoice = 5; + g_Config.bDisableLighting = !g_Config.bDisableLighting; + break; + } +} + +HWND OpenWindow(HWND parent, HINSTANCE hInstance, int width, int height, const TCHAR *title) +{ + wndClass.cbSize = sizeof( wndClass ); + wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + wndClass.lpfnWndProc = WndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = hInstance; + wndClass.hIcon = LoadIcon( NULL, IDI_APPLICATION ); + wndClass.hCursor = NULL; + wndClass.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH ); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = m_szClassName; + wndClass.hIconSm = LoadIcon( NULL, IDI_APPLICATION ); + + m_hInstance = hInstance; + RegisterClassEx( &wndClass ); + + m_hParent = parent; + + m_hWnd = CreateWindow(m_szClassName, title, WS_CHILD, + 0, 0, width, height, m_hParent, NULL, hInstance, NULL); + + return m_hWnd; +} + +void Show() +{ + ShowWindow(m_hWnd, SW_SHOW); + BringWindowToTop(m_hWnd); + UpdateWindow(m_hWnd); + SetFocus(m_hParent); +} + +HWND Create(HWND hParent, HINSTANCE hInstance, const TCHAR *title) +{ + // TODO: + // 1. Remove redundant window manipulation, + // 2. Make DX11 in fullscreen can be overlapped by other dialogs + // 3. Request window sizes which actually make the client area map to a common resolution + HWND Ret; + int x=0, y=0, width=640, height=480; + g_VideoInitialize.pRequestWindowSize(x, y, width, height); + + Ret = OpenWindow(hParent, hInstance, width, height, title); + + if (Ret) + { + Show(); + } + return Ret; +} + +void Close() +{ + if (m_hParent == NULL) + DestroyWindow(m_hWnd); + UnregisterClass(m_szClassName, m_hInstance); +} + +void SetSize(int width, int height) +{ + RECT rc = {0, 0, width, height}; + DWORD style = GetWindowLong(m_hWnd, GWL_STYLE); + AdjustWindowRect(&rc, style, false); + + int w = rc.right - rc.left; + int h = rc.bottom - rc.top; + + rc.left = (1280 - w)/2; + rc.right = rc.left + w; + rc.top = (1024 - h)/2; + rc.bottom = rc.top + h; + MoveWindow(m_hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, TRUE); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/EmuWindow.h b/Source/Plugins/Plugin_VideoMerge/Src/EmuWindow.h new file mode 100644 index 0000000000..a352e12230 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/EmuWindow.h @@ -0,0 +1,21 @@ + +#ifndef _EMUWINDOW_H_ +#define _EMUWINDOW_H_ + +#include + +namespace EmuWindow +{ + +HWND GetWnd(); +HWND GetParentWnd(); +HWND Create(HWND hParent, HINSTANCE hInstance, const TCHAR* title); +void Show(); +void Close(); +void SetSize(int displayWidth, int displayHeight); +bool IsSizing(); +void OSDMenu(WPARAM wParam); + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/FramebufferManager.cpp b/Source/Plugins/Plugin_VideoMerge/Src/FramebufferManager.cpp new file mode 100644 index 0000000000..023b9edf0c --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/FramebufferManager.cpp @@ -0,0 +1,216 @@ + +#include "FramebufferManager.h" + +#include "VideoConfig.h" +#include "Renderer.h" + +#include "Main.h" + +FramebufferManagerBase::VirtualXFBListType FramebufferManagerBase::m_virtualXFBList; // Only used in Virtual XFB mode + +const XFBSourceBase* FramebufferManagerBase::m_overlappingXFBArray[]; + +FramebufferManagerBase::~FramebufferManagerBase() +{ + VirtualXFBListType::iterator + it = m_virtualXFBList.begin(), + vlend = m_virtualXFBList.end(); + for (; it != vlend; ++it) + delete it->xfbSource; +} + +const XFBSourceBase** FramebufferManagerBase::GetXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount) +{ + //if (g_ActiveConfig.bUseRealXFB) + // return getRealXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount); + //else + return getVirtualXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount); +} + +inline bool addrRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper) +{ + return !((aLower >= bUpper) || (bLower >= aUpper)); +} + +void FramebufferManagerBase::CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + //if (g_ActiveConfig.bUseRealXFB) + // copyToRealXFB(xfbAddr, fbWidth, fbHeight, sourceRc); + //else + copyToVirtualXFB(xfbAddr, fbWidth, fbHeight, sourceRc); +} + +void FramebufferManagerBase::replaceVirtualXFB() +{ + VirtualXFBListType::iterator + it = m_virtualXFBList.begin(), + vlend = m_virtualXFBList.end(); + + VirtualXFB *vxfb = &*it; + + const s32 srcLower = vxfb->xfbAddr; + const s32 srcUpper = vxfb->xfbAddr + 2 * vxfb->xfbWidth * vxfb->xfbHeight; + const s32 lineSize = 2 * vxfb->xfbWidth; + + while (++it != vlend) + { + vxfb = &*it; + + const s32 dstLower = vxfb->xfbAddr; + const s32 dstUpper = vxfb->xfbAddr + 2 * vxfb->xfbWidth * vxfb->xfbHeight; + + if (dstLower >= srcLower && dstUpper <= srcUpper) + { + // invalidate the data + vxfb->xfbAddr = 0; + vxfb->xfbHeight = 0; + vxfb->xfbWidth = 0; + } + else if (addrRangesOverlap(srcLower, srcUpper, dstLower, dstUpper)) + { + const s32 upperOverlap = (srcUpper - dstLower) / lineSize; + const s32 lowerOverlap = (dstUpper - srcLower) / lineSize; + + if (upperOverlap > 0 && lowerOverlap < 0) + { + vxfb->xfbAddr += lineSize * upperOverlap; + vxfb->xfbHeight -= upperOverlap; + } + else if (lowerOverlap > 0) + { + vxfb->xfbHeight -= lowerOverlap; + } + } + } +} + +FramebufferManagerBase::VirtualXFBListType::iterator +FramebufferManagerBase::findVirtualXFB(u32 xfbAddr, u32 width, u32 height) +{ + const u32 srcLower = xfbAddr; + const u32 srcUpper = xfbAddr + 2 * width * height; + + VirtualXFBListType::iterator + it = m_virtualXFBList.begin(), + vlend = m_virtualXFBList.end(); + for (; it != vlend; ++it) + { + const VirtualXFB &vxfb = *it; + + const u32 dstLower = vxfb.xfbAddr; + const u32 dstUpper = vxfb.xfbAddr + 2 * vxfb.xfbWidth * vxfb.xfbHeight; + + if (dstLower >= srcLower && dstUpper <= srcUpper) + return it; + } + + // That address is not in the Virtual XFB list. + return m_virtualXFBList.end(); +} + +void FramebufferManagerBase::copyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + const VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, fbWidth, fbHeight); + + VirtualXFB *vxfb = NULL; + + if (m_virtualXFBList.end() == it) + { + if (m_virtualXFBList.size() >= MAX_VIRTUAL_XFB) + { + PanicAlert("Requested creating a new virtual XFB although the maximum number has already been reached! Report this to the devs"); + return; + // TODO, possible alternative to failing: just delete the oldest virtual XFB: + // delete m_virtualXFBList.back().xfbSource; + // m_virtualXFBList.pop_back(); + } + else + { + // create a new Virtual XFB and place it at the front of the list + VirtualXFB v; + m_virtualXFBList.push_front(v); + vxfb = &m_virtualXFBList.front(); + } + } + else + { + vxfb = &*it; + delete vxfb->xfbSource; + } + + vxfb->xfbAddr = xfbAddr; + vxfb->xfbWidth = fbWidth; + vxfb->xfbHeight = fbHeight; + + const float scaleX = RendererBase::GetXFBScaleX(); + const float scaleY = RendererBase::GetXFBScaleY(); + + TargetRectangle targetSource; + targetSource.top = (int)(sourceRc.top * scaleY); + targetSource.bottom = (int)(sourceRc.bottom * scaleY); + targetSource.left = (int)(sourceRc.left * scaleX); + targetSource.right = (int)(sourceRc.right * scaleX); + + const unsigned int target_width = targetSource.right - targetSource.left; + const unsigned int target_height = targetSource.bottom - targetSource.top; + + vxfb->xfbSource = g_framebuffer_manager->CreateXFBSource(target_width, target_height); + if (NULL == vxfb->xfbSource) + { + PanicAlert("Failed to create virtual XFB"); + return; + } + + // why do both of these have a height/width/addr ? + vxfb->xfbSource->srcAddr = xfbAddr; + vxfb->xfbSource->srcWidth = fbWidth; + vxfb->xfbSource->srcHeight = fbHeight; + + vxfb->xfbSource->texWidth = target_width; + vxfb->xfbSource->texHeight = target_height; + + if (m_virtualXFBList.end() != it) + { + // move this Virtual XFB to the front of the list. + m_virtualXFBList.splice(m_virtualXFBList.begin(), m_virtualXFBList, it); + + // keep stale XFB data from being used + replaceVirtualXFB(); + } + + g_renderer->ResetAPIState(); // reset any game specific settings + + vxfb->xfbSource->CopyEFB(RendererBase::ConvertEFBRectangle(sourceRc)); + + g_renderer->RestoreAPIState(); +} + +const XFBSourceBase** FramebufferManagerBase::getVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount) +{ + xfbCount = 0; + + if (0 == m_virtualXFBList.size()) // no Virtual XFBs available + return NULL; + + u32 srcLower = xfbAddr; + u32 srcUpper = xfbAddr + 2 * fbWidth * fbHeight; + + VirtualXFBListType::reverse_iterator + it = m_virtualXFBList.rbegin(), + vlend = m_virtualXFBList.rend(); + for (; it != vlend; ++it) + { + VirtualXFB* vxfb = &*it; + + u32 dstLower = vxfb->xfbAddr; + u32 dstUpper = vxfb->xfbAddr + 2 * vxfb->xfbWidth * vxfb->xfbHeight; + + if (addrRangesOverlap(srcLower, srcUpper, dstLower, dstUpper)) + { + m_overlappingXFBArray[xfbCount] = vxfb->xfbSource; + xfbCount++; + } + } + + return &m_overlappingXFBArray[0]; +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/FramebufferManager.h b/Source/Plugins/Plugin_VideoMerge/Src/FramebufferManager.h new file mode 100644 index 0000000000..1821560e81 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/FramebufferManager.h @@ -0,0 +1,71 @@ + +#ifndef _FRAMEBUFFERMANAGER_H_ +#define _FRAMEBUFFERMANAGER_H_ + +#include + +#include "CommonTypes.h" +#include "VideoCommon.h" + +struct XFBSourceBase +{ + u32 srcAddr; + u32 srcWidth; + u32 srcHeight; + + unsigned int texWidth; + unsigned int texHeight; + + virtual void CopyEFB(const TargetRectangle& efbSource) = 0; + + virtual ~XFBSourceBase() {} + +protected: + XFBSourceBase() : srcAddr(0), srcWidth(0), srcHeight(0), texWidth(0), texHeight(0) {} +}; + +class FramebufferManagerBase +{ +public: + + enum + { + MAX_VIRTUAL_XFB = 8, + }; + + virtual ~FramebufferManagerBase(); + + //virtual void Init(int targetWidth, int targetHeight, int msaaSamples, int msaaCoverageSamples) = 0; + //virtual void Shutdown() = 0; + + static void replaceVirtualXFB(); + + static void CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + + static const XFBSourceBase** GetXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount); + static const XFBSourceBase** getVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount); + +//protected: + struct VirtualXFB + { + // Address and size in GameCube RAM + u32 xfbAddr; + u32 xfbWidth; + u32 xfbHeight; + + XFBSourceBase *xfbSource; + }; + + virtual XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height) = 0; + + typedef std::list VirtualXFBListType; + static VirtualXFBListType m_virtualXFBList; // used in virtual XFB mode + + static VirtualXFBListType::iterator findVirtualXFB(u32 xfbAddr, u32 width, u32 height); + + static void copyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + + static const XFBSourceBase* m_overlappingXFBArray[MAX_VIRTUAL_XFB]; +}; + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/Main.cpp b/Source/Plugins/Plugin_VideoMerge/Src/Main.cpp new file mode 100644 index 0000000000..f5ceada447 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/Main.cpp @@ -0,0 +1,628 @@ + +#include "pluginspecs_video.h" + +// TODO: temporary, just used in Video_Prepare +// comment out to use (try to use :p) OpenGL +#define USE_DX11 + +#include +#include + +// Common +#include "Common.h" +#include "Atomic.h" +#include "Thread.h" +#include "LogManager.h" + +// VideoCommon +#include "VideoConfig.h" +#include "Fifo.h" +#include "OpcodeDecoding.h" +#include "BPStructs.h" +#include "VertexLoaderManager.h" +#include "VertexShaderManager.h" +#include "PixelShaderManager.h" +#include "CommandProcessor.h" +#include "PixelEngine.h" +#include "OnScreenDisplay.h" +#include "VideoState.h" +#include "XFBConvert.h" +#include "DLCache.h" + +// internal crap +#include "Renderer.h" +#include "TextureCache.h" +#include "VertexManager.h" +#include "VertexShaderCache.h" +#include "PixelShaderCache.h" +#include "FramebufferManager.h" + +#ifdef _WIN32 + +#include "DX11/DX11_TextureCache.h" +#include "DX11/DX11_VertexShaderCache.h" +#include "DX11/DX11_PixelShaderCache.h" +#include "DX11/DX11_Render.h" +#include "DX11/DX11_VertexManager.h" + +#include "DX9/DX9_TextureCache.h" +#include "DX9/DX9_VertexShaderCache.h" +#include "DX9/DX9_PixelShaderCache.h" +#include "DX9/DX9_Render.h" +#include "DX9/DX9_VertexManager.h" + +#endif + +#include "OGL/OGL_TextureCache.h" +#include "OGL/OGL_VertexShaderCache.h" +#include "OGL/OGL_PixelShaderCache.h" +#include "OGL/OGL_Render.h" +#include "OGL/OGL_VertexManager.h" + +#include "EmuWindow.h" + +// TODO: ifdef wx this +#include "VideoConfigDiag.h" + +#include "Main.h" + +#define PLUGIN_NAME "Dolphin Video Merge [broken]" +#if defined(DEBUGFAST) +#define PLUGIN_FULL_NAME PLUGIN_NAME" (DebugFast)" +#elif defined(_DEBUG) +#define PLUGIN_FULL_NAME PLUGIN_NAME" (Debug)" +#else +#define PLUGIN_FULL_NAME PLUGIN_NAME +#endif + +HINSTANCE g_hInstance = NULL; +SVideoInitialize g_VideoInitialize; +PLUGIN_GLOBALS *g_globals = NULL; + +const char* const g_gfxapi_names[] = +{ + "Software", + "OpenGL", + "Direct3D 9", + "Direct3D 11", +}; + +#define INI_NAME "gfx_new.ini" + +// TODO: save to ini file +// TODO: move to VideoConfig or something +int g_gfxapi = 3; + +// shits +RendererBase *g_renderer; +TextureCacheBase *g_texture_cache; +VertexManagerBase *g_vertex_manager; +VertexShaderCacheBase* g_vertex_shader_cache; +PixelShaderCacheBase* g_pixel_shader_cache; +FramebufferManagerBase* g_framebuffer_manager; + +static bool s_PluginInitialized = false; +volatile u32 s_swapRequested = false; +static u32 s_efbAccessRequested = false; +static volatile u32 s_FifoShuttingDown = false; + +int frameCount; + +#if defined(HAVE_WX) && HAVE_WX +class wxDLLApp : public wxApp +{ + bool OnInit() + { + return true; + } +}; +IMPLEMENT_APP_NO_MAIN(wxDLLApp) +WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst); +#endif + +#ifdef _WIN32 +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + wxSetInstance(hinstDLL); + wxInitialize(); + break; + + case DLL_PROCESS_DETACH: + wxUninitialize(); + break; + + default: + break; + } + + g_hInstance = hinstDLL; + return true; +} +#endif + +static volatile struct +{ + u32 xfbAddr; + FieldType field; + u32 fbWidth; + u32 fbHeight; +} s_beginFieldArgs; + +static inline bool addrRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper) +{ + return !((aLower >= bUpper) || (bLower >= aUpper)); +} + +// Run from the graphics thread (from Fifo.cpp) +void VideoFifo_CheckSwapRequest() +{ + if(g_ActiveConfig.bUseXFB) + { + if (Common::AtomicLoadAcquire(s_swapRequested)) + { + EFBRectangle rc; + g_renderer->Swap(s_beginFieldArgs.xfbAddr, s_beginFieldArgs.field, + s_beginFieldArgs.fbWidth, s_beginFieldArgs.fbHeight, rc); + Common::AtomicStoreRelease(s_swapRequested, false); + } + } +} + +// Run from the graphics thread (from Fifo.cpp) +void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight) +{ + if (g_ActiveConfig.bUseXFB) + { + if(Common::AtomicLoadAcquire(s_swapRequested)) + { + u32 aLower = xfbAddr; + u32 aUpper = xfbAddr + 2 * fbWidth * fbHeight; + u32 bLower = s_beginFieldArgs.xfbAddr; + u32 bUpper = s_beginFieldArgs.xfbAddr + 2 * s_beginFieldArgs.fbWidth * s_beginFieldArgs.fbHeight; + + if (addrRangesOverlap(aLower, aUpper, bLower, bUpper)) + VideoFifo_CheckSwapRequest(); + } + } +} + +static struct +{ + EFBAccessType type; + u32 x; + u32 y; + u32 Data; +} s_accessEFBArgs; + +static u32 s_AccessEFBResult = 0; + +void VideoFifo_CheckEFBAccess() +{ + if (Common::AtomicLoadAcquire(s_efbAccessRequested)) + { + s_AccessEFBResult = g_renderer->AccessEFB(s_accessEFBArgs.type, s_accessEFBArgs.x, s_accessEFBArgs.y); + + Common::AtomicStoreRelease(s_efbAccessRequested, false); + } +} + +void VideoFifo_CheckAsyncRequest() +{ + VideoFifo_CheckSwapRequest(); + VideoFifo_CheckEFBAccess(); +} + +unsigned int Callback_PeekMessages() +{ + MSG msg; + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + return 0; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return 1; +} + +void UpdateFPSDisplay(const char *text) +{ + char temp[512]; + sprintf_s(temp, 512, "SVN R%s: DX11: %s", svn_rev_str, text); + SetWindowTextA(EmuWindow::GetWnd(), temp); +} + +// GLOBAL I N T E R F A C E +// ____________________________________________________________________________ +// Function: GetDllInfo +// Purpose: This function allows the emulator to gather information +// about the DLL by filling in the PluginInfo structure. +// input: A pointer to a PLUGIN_INFO structure that needs to be +// filled by the function. (see def above) +// output: none +// +void GetDllInfo(PLUGIN_INFO* _pPluginInfo) +{ + memcpy(_pPluginInfo->Name, PLUGIN_FULL_NAME, sizeof(PLUGIN_FULL_NAME)); + _pPluginInfo->Type = PLUGIN_TYPE_VIDEO; + _pPluginInfo->Version = 0x0100; +} + +// ___________________________________________________________________________ +// Function: DllConfig +// Purpose: This function is optional function that is provided +// to allow the user to configure the DLL +// input: A handle to the window that calls this function +// output: none +// +void DllConfig(void *_hParent) +{ +#if defined(HAVE_WX) && HAVE_WX + + VideoConfigDiag* const m_config_diag = new VideoConfigDiag((wxWindow *)_hParent); + m_config_diag->ShowModal(); + m_config_diag->Destroy(); + + g_Config.Save((std::string(File::GetUserPath(D_CONFIG_IDX)) + INI_NAME).c_str()); + + SLEEP(50); // hax to keep Dolphin window from staying hidden +#endif +} + +// ___________________________________________________________________________ +// Function: DllDebugger +// Purpose: Open the debugger +// input: a handle to the window that calls this function +// output: none +// +void* DllDebugger(void *_hParent, bool Show) +{ + // TODO: + return NULL; +} + +// ___________________________________________________________________________ +// Function: DllSetGlobals +// Purpose: Set the pointer for globals variables +// input: a pointer to the global struct +// output: none +// +void SetDllGlobals(PLUGIN_GLOBALS* _pPluginGlobals) +{ + g_globals = _pPluginGlobals; +} + +// ___________________________________________________________________________ +// Function: Initialize +// Purpose: Initialize the plugin +// input: Init +// output: none +// +void Initialize(void *init) +{ + return; + + frameCount = 0; + + g_VideoInitialize = *(SVideoInitialize*)init; + + InitXFBConvTables(); + + g_Config.Load((std::string(File::GetUserPath(D_CONFIG_IDX)) + INI_NAME).c_str()); + g_Config.GameIniLoad(g_globals->game_ini); + UpdateActiveConfig(); + + g_VideoInitialize.pWindowHandle = (void*)EmuWindow::Create((HWND)g_VideoInitialize.pWindowHandle, g_hInstance, _T("Loading - Please wait.")); + if (NULL == g_VideoInitialize.pWindowHandle) + { + ERROR_LOG(VIDEO, "An error has occurred while trying to create the window."); + return; + } + + g_VideoInitialize.pPeekMessages = Callback_PeekMessages; + g_VideoInitialize.pUpdateFPSDisplay = UpdateFPSDisplay; + + *(SVideoInitialize*)init = g_VideoInitialize; + + //OSD::AddMessage("Dolphin ... Video Plugin", 5000); + s_PluginInitialized = true; +} + +// ___________________________________________________________________________ +// Function: Shutdown +// Purpose: This function is called when the emulator is shutting down +// a game allowing the dll to de-initialise. +// input: none +// output: none +// +void Shutdown(void) +{ + s_efbAccessRequested = false; + s_FifoShuttingDown = false; + s_swapRequested = false; + + // VideoCommon + DLCache::Shutdown(); + CommandProcessor::Shutdown(); + PixelShaderManager::Shutdown(); + VertexShaderManager::Shutdown(); + OpcodeDecoder_Shutdown(); + VertexLoaderManager::Shutdown(); + Fifo_Shutdown(); + + // internal interfaces + EmuWindow::Close(); + + s_PluginInitialized = false; + + delete g_pixel_shader_cache; + delete g_vertex_shader_cache; + delete g_vertex_manager; + delete g_texture_cache; + delete g_renderer; +} + +// ___________________________________________________________________________ +// Function: DoState +// Purpose: Saves/load state +// input/output: ptr +// input: mode +// +void DoState(unsigned char **ptr, int mode) +{ + PanicAlert("DoState"); +} + +// ___________________________________________________________________________ +// Function: EmuStateChange +// Purpose: Notifies the plugin of a change in emulation state +// input: newState +// output: none +// +void EmuStateChange(PLUGIN_EMUSTATE newState) +{ + Fifo_RunLoop(newState == PLUGIN_EMUSTATE_PLAY); +} + +// I N T E R F A C E + + +// __________________________________________________________________________________________________ +// Function: Video_Prepare +// Purpose: This function is called from the EmuThread before the +// emulation has started. It is just for threadsensitive +// APIs like OpenGL. +// input: none +// output: none +// +void Video_Prepare(void) +{ + s_efbAccessRequested = false; + s_FifoShuttingDown = false; + s_swapRequested = false; + + switch (g_gfxapi) + { +#ifdef _WIN32 + case GFXAPI_D3D9: + g_renderer = new DX9::Renderer; + g_texture_cache = new DX9::TextureCache; + g_vertex_manager = new DX9::VertexManager; + g_vertex_shader_cache = new DX9::VertexShaderCache; + g_pixel_shader_cache = new DX9::PixelShaderCache; + break; + + case GFXAPI_D3D11: + g_renderer = new DX11::Renderer; + g_texture_cache = new DX11::TextureCache; + g_vertex_manager = new DX11::VertexManager; + g_vertex_shader_cache = new DX11::VertexShaderCache; + g_pixel_shader_cache = new DX11::PixelShaderCache; + break; +#endif + + default: + case GFXAPI_OPENGL: + g_renderer = new OGL::Renderer; + g_texture_cache = new OGL::TextureCache; + g_vertex_manager = new OGL::VertexManager; + g_vertex_shader_cache = new OGL::VertexShaderCache; + g_pixel_shader_cache = new OGL::PixelShaderCache; + break; + } + + // VideoCommon + BPInit(); + Fifo_Init(); + VertexLoaderManager::Init(); + OpcodeDecoder_Init(); + VertexShaderManager::Init(); + PixelShaderManager::Init(); + CommandProcessor::Init(); + PixelEngine::Init(); + DLCache::Init(); + + // tell the host that the window is ready + g_VideoInitialize.pCoreMessage(WM_USER_CREATE); +} + +// __________________________________________________________________________________________________ +// Function: Video_BeginField +// Purpose: When a field begins in the VI emulator, this function tells the video plugin what the +// parameters of the upcoming field are. The video plugin should make sure the previous +// field is on the player's display before returning. +// input: vi parameters of the upcoming field +// output: none +// +void Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight) +{ + if (s_PluginInitialized && g_ActiveConfig.bUseXFB) + { + if (g_VideoInitialize.bOnThread) + { + while (Common::AtomicLoadAcquire(s_swapRequested) && !s_FifoShuttingDown) + //Common::SleepCurrentThread(1); + Common::YieldCPU(); + } + else + VideoFifo_CheckSwapRequest(); + s_beginFieldArgs.xfbAddr = xfbAddr; + s_beginFieldArgs.field = field; + s_beginFieldArgs.fbWidth = fbWidth; + s_beginFieldArgs.fbHeight = fbHeight; + + Common::AtomicStoreRelease(s_swapRequested, true); + } +} + +// __________________________________________________________________________________________________ +// Function: Video_EndField +// Purpose: When a field ends in the VI emulator, this function notifies the video plugin. The video +// has permission to swap the field to the player's display. +// input: none +// output: none +// +void Video_EndField() +{ + return; +} + +// __________________________________________________________________________________________________ +// Function: Video_AccessEFB +// input: type of access (r/w, z/color, ...), x coord, y coord +// output: response to the access request (ex: peek z data at specified coord) +// +u32 Video_AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) +{ + if (s_PluginInitialized) + { + s_accessEFBArgs.type = type; + s_accessEFBArgs.x = x; + s_accessEFBArgs.y = y; + s_accessEFBArgs.Data = InputData; + Common::AtomicStoreRelease(s_efbAccessRequested, true); + + if (g_VideoInitialize.bOnThread) + { + while (Common::AtomicLoadAcquire(s_efbAccessRequested) && !s_FifoShuttingDown) + //Common::SleepCurrentThread(1); + Common::YieldCPU(); + } + else + VideoFifo_CheckEFBAccess(); + + return s_AccessEFBResult; + } + return 0; +} + +// __________________________________________________________________________________________________ +// Function: Video_Screenshot +// input: Filename +// output: true if all was okay +// +void Video_Screenshot(const char *_szFilename) +{ + PanicAlert("Screenshots are not yet supported."); + return; +} + +// __________________________________________________________________________________________________ +// Function: Video_EnterLoop +// Purpose: Enters the video fifo dispatch loop. This is only used in Dual Core mode. +// input: none +// output: none +// +void Video_EnterLoop() +{ + Fifo_EnterLoop(g_VideoInitialize); +} + +// __________________________________________________________________________________________________ +// Function: Video_ExitLoop +// Purpose: Exits the video dispatch loop. This is only used in Dual Core mode. +// input: none +// output: none +// +void Video_ExitLoop() +{ + Fifo_ExitLoop(); + s_FifoShuttingDown = true; +} + +// __________________________________________________________________________________________________ +// Function: Video_SetRendering +// Purpose: Sets video rendering on and off. Currently used for frame skipping +// input: Enabled toggle +// output: none +// +void Video_SetRendering(bool bEnabled) +{ + PanicAlert("SetRendering is not yet supported."); +} + +// __________________________________________________________________________________________________ +// Function: Video_AddMessage +// Purpose: Adds a message to the display queue, to be shown forthe specified time +// input: pointer to the null-terminated string, time in milliseconds +// output: none +// +void Video_AddMessage(const char* pstr, unsigned int milliseconds) +{ + return; +} + +void Video_CommandProcessorRead16(u16& _rReturnValue, const u32 _Address) +{ + CommandProcessor::Read16(_rReturnValue, _Address); +} + +void Video_CommandProcessorWrite16(const u16 _Data, const u32 _Address) +{ + CommandProcessor::Write16(_Data, _Address); +} + +void Video_PixelEngineRead16(u16& _rReturnValue, const u32 _Address) +{ + PixelEngine::Read16(_rReturnValue, _Address); +} + +void Video_PixelEngineWrite16(const u16 _Data, const u32 _Address) +{ + PixelEngine::Write16(_Data, _Address); +} + +void Video_PixelEngineWrite32(const u32 _Data, const u32 _Address) +{ + PixelEngine::Write32(_Data, _Address); +} + +void Video_GatherPipeBursted(void) +{ + CommandProcessor::GatherPipeBursted(); +} + +void Video_WaitForFrameFinish(void) +{ + CommandProcessor::WaitForFrameFinish(); +} + +// __________________________________________________________________________________________________ +// Function: Video_IsFifoBusy +// Purpose: Return if the FIFO is proecessing data, that is used for sync gfx thread and emulator +// thread in CoreTiming +// input: none +// output: bool +// +bool Video_IsFifoBusy(void) +{ + return CommandProcessor::isFifoBusy; +} + +void Video_AbortFrame(void) +{ + CommandProcessor::AbortFrame(); +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/Main.h b/Source/Plugins/Plugin_VideoMerge/Src/Main.h new file mode 100644 index 0000000000..d869cedf0a --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/Main.h @@ -0,0 +1,57 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#include "PluginSpecs_Video.h" + +#include "Renderer.h" +#include "TextureCache.h" +#include "VertexManager.h" +#include "PixelShaderCache.h" +#include "VertexShaderCache.h" +#include "FramebufferManager.h" + +extern SVideoInitialize g_VideoInitialize; +extern volatile u32 s_swapRequested; +extern PLUGIN_GLOBALS *g_globals; + +extern RendererBase *g_renderer; +extern TextureCacheBase *g_texture_cache; +extern VertexManagerBase *g_vertex_manager; +extern VertexShaderCacheBase* g_vertex_shader_cache; +extern PixelShaderCacheBase* g_pixel_shader_cache; +extern FramebufferManagerBase* g_framebuffer_manager; + +extern int frameCount; + +void VideoFifo_CheckEFBAccess(); +void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight); + +enum +{ + GFXAPI_SOFTWARE, + GFXAPI_OPENGL, + GFXAPI_D3D9, + GFXAPI_D3D11, +}; + +extern const char* const g_gfxapi_names[]; +extern int g_gfxapi; + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_FramebufferManager.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_FramebufferManager.cpp new file mode 100644 index 0000000000..f03edef9f7 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_FramebufferManager.cpp @@ -0,0 +1,385 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// OGL +#include "OGL_Render.h" +#include "OGL_XFB.h" +#include "OGL_FramebufferManager.h" +#include "OGL_TextureConverter.h" + +namespace OGL +{ + +extern bool s_bHaveFramebufferBlit; // comes from Render.cpp + +int FramebufferManager::m_targetWidth; +int FramebufferManager::m_targetHeight; +int FramebufferManager::m_msaaSamples; +int FramebufferManager::m_msaaCoverageSamples; + +GLuint FramebufferManager::m_efbFramebuffer; +GLuint FramebufferManager::m_efbColor; // Renderbuffer in MSAA mode; Texture otherwise +GLuint FramebufferManager::m_efbDepth; // Renderbuffer in MSAA mode; Texture otherwise + +// Only used in MSAA mode. +GLuint FramebufferManager::m_resolvedFramebuffer; +GLuint FramebufferManager::m_resolvedColorTexture; +GLuint FramebufferManager::m_resolvedDepthTexture; + +GLuint FramebufferManager::m_xfbFramebuffer; // Only used in MSAA mode +XFBSource FramebufferManager::m_realXFBSource; // Only used in Real XFB mode + +FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int msaaSamples, int msaaCoverageSamples) +{ + // m_efbFramebuffer(0), + // m_efbColor(0), + // m_efbDepth(0), + // m_resolvedFramebuffer(0), + // m_resolvedColorTexture(0), + // m_resolvedDepthTexture(0), + // m_xfbFramebuffer(0) + + m_targetWidth = targetWidth; + m_targetHeight = targetHeight; + m_msaaSamples = msaaSamples; + m_msaaCoverageSamples = msaaCoverageSamples; + + // The EFB can be set to different pixel formats by the game through the + // BPMEM_ZCOMPARE register (which should probably have a different name). + // They are: + // - 24-bit RGB (8-bit components) with 24-bit Z + // - 24-bit RGBA (6-bit components) with 24-bit Z + // - Multisampled 16-bit RGB (5-6-5 format) with 16-bit Z + // We only use one EFB format here: 32-bit ARGB with 24-bit Z. + // Multisampling depends on user settings. + // The distinction becomes important for certain operations, i.e. the + // alpha channel should be ignored if the EFB does not have one. + + // Create EFB target. + + glGenFramebuffersEXT(1, &m_efbFramebuffer); + + if (m_msaaSamples <= 1) + { + // EFB targets will be textures in non-MSAA mode. + + GLuint glObj[2]; + glGenTextures(2, glObj); + m_efbColor = glObj[0]; + m_efbDepth = glObj[1]; + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_efbColor); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_efbDepth); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + + // Bind target textures to the EFB framebuffer. + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_efbFramebuffer); + + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, m_efbColor, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, m_efbDepth, 0); + + GL_REPORT_FBO_ERROR(); + } + else + { + // EFB targets will be renderbuffers in MSAA mode (required by OpenGL). + // Resolve targets will be created to transfer EFB to RAM textures. + // XFB framebuffer will be created to transfer EFB to XFB texture. + + // Create EFB target renderbuffers. + + GLuint glObj[2]; + glGenRenderbuffersEXT(2, glObj); + m_efbColor = glObj[0]; + m_efbDepth = glObj[1]; + + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_efbColor); + if (m_msaaCoverageSamples) + glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER_EXT, m_msaaCoverageSamples, m_msaaSamples, GL_RGBA8, m_targetWidth, m_targetHeight); + else + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, m_msaaSamples, GL_RGBA8, m_targetWidth, m_targetHeight); + + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_efbDepth); + if (m_msaaCoverageSamples) + glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER_EXT, m_msaaCoverageSamples, m_msaaSamples, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight); + else + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, m_msaaSamples, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight); + + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + // Bind target renderbuffers to EFB framebuffer. + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_efbFramebuffer); + + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_efbColor); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_efbDepth); + + GL_REPORT_FBO_ERROR(); + + // Create resolved targets for transferring multisampled EFB to texture. + + glGenFramebuffersEXT(1, &m_resolvedFramebuffer); + + glGenTextures(2, glObj); + m_resolvedColorTexture = glObj[0]; + m_resolvedDepthTexture = glObj[1]; + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_resolvedColorTexture); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_resolvedDepthTexture); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + + // Bind resolved textures to resolved framebuffer. + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_resolvedFramebuffer); + + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, m_resolvedColorTexture, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, m_resolvedDepthTexture, 0); + + GL_REPORT_FBO_ERROR(); + + // Return to EFB framebuffer. + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_efbFramebuffer); + } + + // Create XFB framebuffer; targets will be created elsewhere. + + glGenFramebuffersEXT(1, &m_xfbFramebuffer); + + // EFB framebuffer is currently bound. +} + +FramebufferManager::~FramebufferManager() +{ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + GLuint glObj[3]; + + // Note: OpenGL deletion functions silently ignore parameters of "0". + + glObj[0] = m_efbFramebuffer; + glObj[1] = m_resolvedFramebuffer; + glObj[2] = m_xfbFramebuffer; + glDeleteFramebuffersEXT(3, glObj); + m_efbFramebuffer = 0; + m_xfbFramebuffer = 0; + + glObj[0] = m_resolvedColorTexture; + glObj[1] = m_resolvedDepthTexture; + glObj[2] = m_realXFBSource.texture; + glDeleteTextures(3, glObj); + m_resolvedColorTexture = 0; + m_resolvedDepthTexture = 0; + m_realXFBSource.texture = 0; + + glObj[0] = m_efbColor; + glObj[1] = m_efbDepth; + if (m_msaaSamples <= 1) + glDeleteTextures(2, glObj); + else + glDeleteRenderbuffersEXT(2, glObj); + m_efbColor = 0; + m_efbDepth = 0; +} + +XFBSourceBase* FramebufferManager::CreateXFBSource(unsigned int target_width, unsigned int target_height) +{ + XFBSource* const xfbs = new XFBSource; + + glGenTextures(1, &xfbs->texture); + + return xfbs; +} + +void XFBSource::CopyEFB(const TargetRectangle& efbSource) +{ + // Copy EFB to XFB texture + +#if 0 + if (m_msaaSamples <= 1) +#else + if (!s_bHaveFramebufferBlit) +#endif + { + // Just copy the EFB directly. + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FramebufferManager::m_efbFramebuffer); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture); + glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, 0, 0, texWidth, texHeight, 0); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + } + else + { + // OpenGL cannot copy directly from a multisampled framebuffer, so use + // EXT_framebuffer_blit. + + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, FramebufferManager::m_efbFramebuffer); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, FramebufferManager::m_xfbFramebuffer); + + // Bind texture. + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_RECTANGLE_ARB, texture, 0); + + GL_REPORT_FBO_ERROR(); + + glBlitFramebufferEXT(0, 0, texWidth, texHeight, 0, 0, texWidth, + texHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + // Unbind texture. + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0); + + // Return to EFB. + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FramebufferManager::m_efbFramebuffer); + } +} + +GLuint FramebufferManager::GetEFBColorTexture(const EFBRectangle& sourceRc) +{ + if (m_msaaSamples <= 1) + { + return m_efbColor; + } + else + { + // Transfer the EFB to a resolved texture. EXT_framebuffer_blit is + // required. + + TargetRectangle targetRc = RendererBase::ConvertEFBRectangle(sourceRc); + targetRc.ClampLL(0, 0, m_targetWidth, m_targetHeight); + + // Resolve. + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_efbFramebuffer); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_resolvedFramebuffer); + glBlitFramebufferEXT( + targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, + targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, + GL_COLOR_BUFFER_BIT, GL_NEAREST + ); + + // Return to EFB. + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_efbFramebuffer); + + return m_resolvedColorTexture; + } +} + +GLuint FramebufferManager::GetEFBDepthTexture(const EFBRectangle& sourceRc) +{ + if (m_msaaSamples <= 1) + { + return m_efbDepth; + } + else + { + // Transfer the EFB to a resolved texture. EXT_framebuffer_blit is + // required. + + TargetRectangle targetRc = RendererBase::ConvertEFBRectangle(sourceRc); + targetRc.ClampLL(0, 0, m_targetWidth, m_targetHeight); + + // Resolve. + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_efbFramebuffer); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_resolvedFramebuffer); + glBlitFramebufferEXT( + targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, + targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, + GL_DEPTH_BUFFER_BIT, GL_NEAREST + ); + + // Return to EFB. + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_efbFramebuffer); + + return m_resolvedDepthTexture; + } +} + +void FramebufferManager::copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + u8* pXFB = Memory_GetPtr(xfbAddr); + if (!pXFB) + { + WARN_LOG(VIDEO, "Tried to copy to invalid XFB address"); + return; + } + + XFB_Write(pXFB, sourceRc, fbWidth, fbHeight); +} + +const XFBSourceBase** FramebufferManager::getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount) +{ + xfbCount = 1; + + m_realXFBSource.texWidth = MAX_XFB_WIDTH; + m_realXFBSource.texHeight = MAX_XFB_HEIGHT; + + m_realXFBSource.srcAddr = xfbAddr; + m_realXFBSource.srcWidth = fbWidth; + m_realXFBSource.srcHeight = fbHeight; + + // OpenGL texture coordinates originate at the lower left, which is why + // sourceRc.top = fbHeight and sourceRc.bottom = 0. + m_realXFBSource.sourceRc.left = 0; + m_realXFBSource.sourceRc.top = fbHeight; + m_realXFBSource.sourceRc.right = fbWidth; + m_realXFBSource.sourceRc.bottom = 0; + + if (!m_realXFBSource.texture) + { + glGenTextures(1, &m_realXFBSource.texture); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_realXFBSource.texture); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, MAX_XFB_WIDTH, MAX_XFB_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + } + + // Decode YUYV data from GameCube RAM + TextureConverter::DecodeToTexture(xfbAddr, fbWidth, fbHeight, m_realXFBSource.texture); + + m_overlappingXFBArray[0] = &m_realXFBSource; + + return &m_overlappingXFBArray[0]; +} + +void FramebufferManager::SetFramebuffer(GLuint fb) +{ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb != 0 ? fb : GetEFBFramebuffer()); +} + +// Apply AA if enabled +GLuint FramebufferManager::ResolveAndGetRenderTarget(const EFBRectangle &source_rect) +{ + return GetEFBColorTexture(source_rect); +} + +GLuint FramebufferManager::ResolveAndGetDepthTarget(const EFBRectangle &source_rect) +{ + return GetEFBDepthTexture(source_rect); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_FramebufferManager.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_FramebufferManager.h new file mode 100644 index 0000000000..70571d4f25 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_FramebufferManager.h @@ -0,0 +1,135 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_FRAMEBUFFERMANAGER_H_ +#define _OGL_FRAMEBUFFERMANAGER_H_ + +#include + +#include "OGL_GLUtil.h" + +#include "../FramebufferManager.h" + +namespace OGL +{ + +// On the GameCube, the game sends a request for the graphics processor to +// transfer its internal EFB (Embedded Framebuffer) to an area in GameCube RAM +// called the XFB (External Framebuffer). The size and location of the XFB is +// decided at the time of the copy, and the format is always YUYV. The video +// interface is given a pointer to the XFB, which will be decoded and +// displayed on the TV. +// +// There are two ways for Dolphin to emulate this: +// +// Real XFB mode: +// +// Dolphin will behave like the GameCube and encode the EFB to +// a portion of GameCube RAM. The emulated video interface will decode the data +// for output to the screen. +// +// Advantages: Behaves exactly like the GameCube. +// Disadvantages: Resolution will be limited. +// +// Virtual XFB mode: +// +// When a request is made to copy the EFB to an XFB, Dolphin +// will remember the RAM location and size of the XFB in a Virtual XFB list. +// The video interface will look up the XFB in the list and use the enhanced +// data stored there, if available. +// +// Advantages: Enables high resolution graphics, better than real hardware. +// Disadvantages: If the GameCube CPU writes directly to the XFB (which is +// possible but uncommon), the Virtual XFB will not capture this information. + +// There may be multiple XFBs in GameCube RAM. This is the maximum number to +// virtualize. + +inline bool addrRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper) +{ + return !((aLower >= bUpper) || (bLower >= aUpper)); +} + +struct XFBSource : XFBSourceBase +{ + XFBSource() : texture(0) {} + + void CopyEFB(const TargetRectangle& efbSource); + + GLuint texture; + TargetRectangle sourceRc; +}; + +class FramebufferManager : public ::FramebufferManagerBase +{ + friend struct XFBSource; + +public: + FramebufferManager(int targetWidth, int targetHeight, int msaaSamples, int msaaCoverageSamples); + ~FramebufferManager(); + + // To get the EFB in texture form, these functions may have to transfer + // the EFB to a resolved texture first. + static GLuint GetEFBColorTexture(const EFBRectangle& sourceRc); + static GLuint GetEFBDepthTexture(const EFBRectangle& sourceRc); + + static GLuint GetEFBFramebuffer() { return m_efbFramebuffer; } + + // Resolved framebuffer is only used in MSAA mode. + static GLuint GetResolvedFramebuffer() { return m_resolvedFramebuffer; } + + static void SetFramebuffer(GLuint fb); + + // If in MSAA mode, this will perform a resolve of the specified rectangle, and return the resolve target as a texture ID. + // Thus, this call may be expensive. Don't repeat it unnecessarily. + // If not in MSAA mode, will just return the render target texture ID. + // After calling this, before you render anything else, you MUST bind the framebuffer you want to draw to. + static GLuint ResolveAndGetRenderTarget(const EFBRectangle &rect); + + // Same as above but for the depth Target. + // After calling this, before you render anything else, you MUST bind the framebuffer you want to draw to. + static GLuint ResolveAndGetDepthTarget(const EFBRectangle &rect); + + XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height); + +protected: + static GLuint m_efbFramebuffer; + static GLuint m_xfbFramebuffer; // Only used in MSAA mode + +private: + static void copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + static const XFBSourceBase** getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount); + + static int m_targetWidth; + static int m_targetHeight; + static int m_msaaSamples; + static int m_msaaCoverageSamples; + + static GLuint m_efbColor; // Renderbuffer in MSAA mode; Texture otherwise + static GLuint m_efbDepth; // Renderbuffer in MSAA mode; Texture otherwise + + // Only used in MSAA mode. + static GLuint m_resolvedFramebuffer; + static GLuint m_resolvedColorTexture; + static GLuint m_resolvedDepthTexture; + + static XFBSource m_realXFBSource; // Only used in Real XFB mode +}; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_GLUtil.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_GLUtil.cpp new file mode 100644 index 0000000000..8664443fb1 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_GLUtil.cpp @@ -0,0 +1,514 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Common +#include "IniFile.h" +#include "Setup.h" + +// VideoCommon +#include "VideoConfig.h" + +// OGL +#include "OGL_Render.h" +#include "OGL_GLUtil.h" + +#ifdef _WIN32 +#include "../EmuWindow.h" +extern HINSTANCE g_hInstance; +#endif + +namespace OGL +{ + +#ifdef _WIN32 +static HDC hDC = NULL; // Private GDI Device Context +static HGLRC hRC = NULL; // Permanent Rendering Context +#else +GLWindow GLWin; +#endif + +// Handles OpenGL and the window + +// Window dimensions. +static int s_backbuffer_width; +static int s_backbuffer_height; + +void OpenGL_SwapBuffers() +{ +#if defined(USE_WX) && USE_WX + GLWin.glCanvas->SwapBuffers(); +#elif defined(__APPLE__) + cocoaGLSwap(GLWin.cocoaCtx,GLWin.cocoaWin); +#elif defined(_WIN32) + SwapBuffers(hDC); +#elif defined(HAVE_X11) && HAVE_X11 + glXSwapBuffers(GLWin.dpy, GLWin.win); +#endif +} + +u32 OpenGL_GetBackbufferWidth() +{ + return s_backbuffer_width; +} + +u32 OpenGL_GetBackbufferHeight() +{ + return s_backbuffer_height; +} + +void OpenGL_SetWindowText(const char *text) +{ +#if defined(USE_WX) && USE_WX + // GLWin.frame->SetTitle(wxString::FromAscii(text)); +#elif defined(__APPLE__) + cocoaGLSetTitle(GLWin.cocoaWin, text); +#elif defined(_WIN32) + // TODO convert text to unicode and change SetWindowTextA to SetWindowText + SetWindowTextA(EmuWindow::GetWnd(), text); +#elif defined(HAVE_X11) && HAVE_X11 + // Tell X to ask the window manager to set the window title. + // (X itself doesn't provide window title functionality.) + XStoreName(GLWin.dpy, GLWin.win, text); +#endif +} + +#if defined(HAVE_X11) && HAVE_X11 +THREAD_RETURN XEventThread(void *pArg); + +void CreateXWindow (void) +{ + Atom wmProtocols[1]; + + // Setup window attributes + GLWin.attr.colormap = XCreateColormap(GLWin.dpy, + GLWin.parent, GLWin.vi->visual, AllocNone); + GLWin.attr.event_mask = KeyPressMask | StructureNotifyMask | FocusChangeMask; + GLWin.attr.background_pixel = BlackPixel(GLWin.dpy, GLWin.screen); + GLWin.attr.border_pixel = 0; + + // Create the window + GLWin.win = XCreateWindow(GLWin.dpy, GLWin.parent, + GLWin.x, GLWin.y, GLWin.width, GLWin.height, 0, GLWin.vi->depth, InputOutput, GLWin.vi->visual, + CWBorderPixel | CWBackPixel | CWColormap | CWEventMask, &GLWin.attr); + wmProtocols[0] = XInternAtom(GLWin.dpy, "WM_DELETE_WINDOW", True); + XSetWMProtocols(GLWin.dpy, GLWin.win, wmProtocols, 1); + XSetStandardProperties(GLWin.dpy, GLWin.win, "GPU", "GPU", None, NULL, 0, NULL); + XMapRaised(GLWin.dpy, GLWin.win); + XSync(GLWin.dpy, True); + + GLWin.xEventThread = new Common::Thread(XEventThread, NULL); +} + +void DestroyXWindow(void) +{ + XUnmapWindow(GLWin.dpy, GLWin.win); + GLWin.win = 0; + XFreeColormap(GLWin.dpy, GLWin.attr.colormap); + if (GLWin.xEventThread) + GLWin.xEventThread->WaitForDeath(); + GLWin.xEventThread = NULL; +} + +THREAD_RETURN XEventThread(void *pArg) +{ + while (GLWin.win) + { + XEvent event; + KeySym key; + for (int num_events = XPending(GLWin.dpy); num_events > 0; num_events--) { + XNextEvent(GLWin.dpy, &event); + switch(event.type) { + case KeyPress: + key = XLookupKeysym((XKeyEvent*)&event, 0); + switch (key) + { + case XK_3: + OSDChoice = 1; + // Toggle native resolution + if (!(g_Config.bNativeResolution || g_Config.b2xResolution)) + g_Config.bNativeResolution = true; + else if (g_Config.bNativeResolution && Renderer::AllowCustom()) + { + g_Config.bNativeResolution = false; + if (Renderer::Allow2x()) + g_Config.b2xResolution = true; + } + else if (Renderer::AllowCustom()) + g_Config.b2xResolution = false; + break; + case XK_4: + OSDChoice = 2; + // Toggle aspect ratio + g_Config.iAspectRatio = (g_Config.iAspectRatio + 1) & 3; + break; + case XK_5: + OSDChoice = 3; + // Toggle EFB copy + if (g_Config.bEFBCopyDisable || g_Config.bCopyEFBToTexture) + { + g_Config.bEFBCopyDisable = !g_Config.bEFBCopyDisable; + g_Config.bCopyEFBToTexture = false; + } + else + { + g_Config.bCopyEFBToTexture = !g_Config.bCopyEFBToTexture; + } + break; + case XK_6: + OSDChoice = 4; + g_Config.bDisableFog = !g_Config.bDisableFog; + break; + case XK_7: + OSDChoice = 5; + g_Config.bDisableLighting = !g_Config.bDisableLighting; + break; + default: + break; + } + break; + case ConfigureNotify: + Window winDummy; + unsigned int borderDummy, depthDummy; + XGetGeometry(GLWin.dpy, GLWin.win, &winDummy, &GLWin.x, &GLWin.y, + &GLWin.width, &GLWin.height, &borderDummy, &depthDummy); + s_backbuffer_width = GLWin.width; + s_backbuffer_height = GLWin.height; + break; + case ClientMessage: + if ((unsigned long) event.xclient.data.l[0] == XInternAtom(GLWin.dpy, "WM_DELETE_WINDOW", False)) + g_VideoInitialize.pCoreMessage(WM_USER_STOP); + if ((unsigned long) event.xclient.data.l[0] == XInternAtom(GLWin.dpy, "RESIZE", False)) + XMoveResizeWindow(GLWin.dpy, GLWin.win, event.xclient.data.l[1], + event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]); + break; + default: + break; + } + } + Common::SleepCurrentThread(20); + } + return 0; +} +#endif + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool OpenGL_Create(SVideoInitialize &_VideoInitialize, int _iwidth, int _iheight) +{ + int _tx, _ty, _twidth, _theight; + g_VideoInitialize.pRequestWindowSize(_tx, _ty, _twidth, _theight); + + // Control window size and picture scaling + s_backbuffer_width = _twidth; + s_backbuffer_height = _theight; + +#if defined(USE_WX) && USE_WX + int args[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0}; + + wxSize size(_twidth, _theight); + + GLWin.panel = (wxPanel *)g_VideoInitialize.pWindowHandle; + + GLWin.glCanvas = new wxGLCanvas(GLWin.panel, wxID_ANY, args, + wxPoint(0,0), size, wxSUNKEN_BORDER); + GLWin.glCtxt = new wxGLContext(GLWin.glCanvas); + GLWin.glCanvas->Show(TRUE); + + GLWin.glCanvas->SetCurrent(*GLWin.glCtxt); + +#elif defined(__APPLE__) + GLWin.width = s_backbuffer_width; + GLWin.height = s_backbuffer_height; + GLWin.cocoaWin = cocoaGLCreateWindow(GLWin.width, GLWin.height); + GLWin.cocoaCtx = cocoaGLInit(g_Config.iMultisampleMode); + +#elif defined(_WIN32) + + PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be + { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, // Version Number + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, // Must Support Double Buffering + PFD_TYPE_RGBA, // Request An RGBA Format + 32, // Select Our Color Depth + 0, 0, 0, 0, 0, 0, // Color Bits Ignored + 0, // 8bit Alpha Buffer + 0, // Shift Bit Ignored + 0, // No Accumulation Buffer + 0, 0, 0, 0, // Accumulation Bits Ignored + 24, // 24Bit Z-Buffer (Depth Buffer) + 8, // 8bit Stencil Buffer + 0, // No Auxiliary Buffer + PFD_MAIN_PLANE, // Main Drawing Layer + 0, // Reserved + 0, 0, 0 // Layer Masks Ignored + }; + + GLuint PixelFormat; // Holds The Results After Searching For A Match + + if (!(hDC=GetDC(EmuWindow::GetWnd()))) { + PanicAlert("(1) Can't create an OpenGL Device context. Fail."); + return false; + } + if (!(PixelFormat = ChoosePixelFormat(hDC,&pfd))) { + PanicAlert("(2) Can't find a suitable PixelFormat."); + return false; + } + if (!SetPixelFormat(hDC, PixelFormat, &pfd)) { + PanicAlert("(3) Can't set the PixelFormat."); + return false; + } + if (!(hRC = wglCreateContext(hDC))) { + PanicAlert("(4) Can't create an OpenGL rendering context."); + return false; + } + // -------------------------------------- + +#elif defined(HAVE_X11) && HAVE_X11 + int glxMajorVersion, glxMinorVersion; + + // attributes for a single buffered visual in RGBA format with at least + // 8 bits per color and a 24 bit depth buffer + int attrListSgl[] = {GLX_RGBA, GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_DEPTH_SIZE, 24, + None}; + + // attributes for a double buffered visual in RGBA format with at least + // 8 bits per color and a 24 bit depth buffer + int attrListDbl[] = {GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_SAMPLE_BUFFERS_ARB, g_Config.iMultisampleMode != MULTISAMPLE_OFF?1:0, + GLX_SAMPLES_ARB, g_Config.iMultisampleMode != MULTISAMPLE_OFF?1:0, + None }; + + int attrListDefault[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 1, + None }; + + GLWin.dpy = XOpenDisplay(0); + GLWin.parent = (Window)g_VideoInitialize.pWindowHandle; + GLWin.screen = DefaultScreen(GLWin.dpy); + if (GLWin.parent == 0) + GLWin.parent = RootWindow(GLWin.dpy, GLWin.screen); + + glXQueryVersion(GLWin.dpy, &glxMajorVersion, &glxMinorVersion); + NOTICE_LOG(VIDEO, "glX-Version %d.%d", glxMajorVersion, glxMinorVersion); + + // Get an appropriate visual + GLWin.vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListDbl); + if (GLWin.vi == NULL) + { + GLWin.vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListSgl); + if (GLWin.vi != NULL) + { + ERROR_LOG(VIDEO, "Only Singlebuffered Visual!"); + } + else + { + GLWin.vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListDefault); + if (GLWin.vi == NULL) + { + ERROR_LOG(VIDEO, "Could not choose visual (glXChooseVisual)"); + exit(0); + } + } + } + else + NOTICE_LOG(VIDEO, "Got Doublebuffered Visual!"); + + // Create a GLX context. + GLWin.ctx = glXCreateContext(GLWin.dpy, GLWin.vi, 0, GL_TRUE); + if (!GLWin.ctx) + { + PanicAlert("Couldn't Create GLX context.Quit"); + exit(0); // TODO: Don't bring down entire Emu + } + + GLWin.x = _tx; + GLWin.y = _ty; + GLWin.width = _twidth; + GLWin.height = _theight; + + CreateXWindow(); + g_VideoInitialize.pWindowHandle = (void *)GLWin.win; +#endif + return true; +} + +bool OpenGL_MakeCurrent() +{ + // connect the glx-context to the window +#if defined(USE_WX) && USE_WX + GLWin.glCanvas->SetCurrent(*GLWin.glCtxt); +#elif defined(__APPLE__) + cocoaGLMakeCurrent(GLWin.cocoaCtx,GLWin.cocoaWin); +#elif defined(_WIN32) + return wglMakeCurrent(hDC,hRC) ? true : false; +#elif defined(HAVE_X11) && HAVE_X11 +#if defined(HAVE_WX) && (HAVE_WX) + g_VideoInitialize.pRequestWindowSize(GLWin.x, GLWin.y, (int&)GLWin.width, (int&)GLWin.height); + XMoveResizeWindow(GLWin.dpy, GLWin.win, GLWin.x, GLWin.y, GLWin.width, GLWin.height); +#endif + return glXMakeCurrent(GLWin.dpy, GLWin.win, GLWin.ctx); +#endif + return true; +} + +// Update window width, size and etc. Called from Render.cpp +void OpenGL_Update() +{ +#if defined(USE_WX) && USE_WX + GLWin.glCanvas->GetSize((int *)&GLWin.width, (int *)&GLWin.height); + s_backbuffer_width = GLWin.width; + s_backbuffer_height = GLWin.height; +#elif defined(__APPLE__) + + // Is anything needed here? + +#elif defined(_WIN32) + RECT rcWindow; + if (!EmuWindow::GetParentWnd()) + { + // We are not rendering to a child window - use client size. + GetClientRect(EmuWindow::GetWnd(), &rcWindow); + } + else + { + // We are rendering to a child window - use parent size. + GetWindowRect(EmuWindow::GetParentWnd(), &rcWindow); + } + + // Get the new window width and height + // See below for documentation + int width = rcWindow.right - rcWindow.left; + int height = rcWindow.bottom - rcWindow.top; + + // If we are rendering to a child window + if (EmuWindow::GetParentWnd() != 0 && (s_backbuffer_width != width || s_backbuffer_height != height) && width >= 4 && height >= 4) + { + ::MoveWindow(EmuWindow::GetWnd(), 0, 0, width, height, FALSE); + s_backbuffer_width = width; + s_backbuffer_height = height; + } +#endif +} + + +// Close plugin +void OpenGL_Shutdown() +{ +#if defined(USE_WX) && USE_WX + delete GLWin.glCanvas; +#elif defined(__APPLE__) + cocoaGLDeleteWindow(GLWin.cocoaWin); + cocoaGLDelete(GLWin.cocoaCtx); + +#elif defined(_WIN32) + if (hRC) // Do We Have A Rendering Context? + { + if (!wglMakeCurrent(NULL,NULL)) // Are We Able To Release The DC And RC Contexts? + { + // [F|RES]: if this fails i dont see the message box and + // cant get out of the modal state so i disable it. + // This function fails only if i render to main window + // MessageBox(NULL,"Release Of DC And RC Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); + } + + if (!wglDeleteContext(hRC)) // Are We Able To Delete The RC? + { + ERROR_LOG(VIDEO, "Release Rendering Context Failed."); + } + hRC = NULL; // Set RC To NULL + } + + if (hDC && !ReleaseDC(EmuWindow::GetWnd(), hDC)) // Are We Able To Release The DC + { + ERROR_LOG(VIDEO, "Release Device Context Failed."); + hDC = NULL; // Set DC To NULL + } + EmuWindow::Close(); +#elif defined(HAVE_X11) && HAVE_X11 + DestroyXWindow(); + if (GLWin.ctx && !glXMakeCurrent(GLWin.dpy, None, NULL)) + NOTICE_LOG(VIDEO, "Could not release drawing context."); + if (GLWin.ctx) + { + glXDestroyContext(GLWin.dpy, GLWin.ctx); + XCloseDisplay(GLWin.dpy); + GLWin.ctx = NULL; + } +#endif +} + +GLuint OpenGL_ReportGLError(const char *function, const char *file, int line) +{ + GLint err = glGetError(); + if (err != GL_NO_ERROR) + { + ERROR_LOG(VIDEO, "%s:%d: (%s) OpenGL error 0x%x - %s\n", file, line, function, err, gluErrorString(err)); + } + return err; +} + +void OpenGL_ReportARBProgramError() +{ + const GLubyte* pstr = glGetString(GL_PROGRAM_ERROR_STRING_ARB); + if (pstr != NULL && pstr[0] != 0) + { + GLint loc = 0; + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &loc); + ERROR_LOG(VIDEO, "program error at %d: ", loc); + ERROR_LOG(VIDEO, (char*)pstr); + ERROR_LOG(VIDEO, ""); + } +} + +bool OpenGL_ReportFBOError(const char *function, const char *file, int line) +{ + unsigned int fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + const char *error = "-"; + switch (fbo_status) + { + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: error = "INCOMPLETE_ATTACHMENT_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error = "INCOMPLETE_MISSING_ATTACHMENT_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error = "INCOMPLETE_DIMENSIONS_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error = "INCOMPLETE_FORMATS_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error = "INCOMPLETE_DRAW_BUFFER_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error = "INCOMPLETE_READ_BUFFER_EXT"; break; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: error = "UNSUPPORTED_EXT"; break; + } + ERROR_LOG(VIDEO, "%s:%d: (%s) OpenGL FBO error - %s\n", file, line, function, error); + return false; + } + return true; +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_GLUtil.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_GLUtil.h new file mode 100644 index 0000000000..1255fd6fca --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_GLUtil.h @@ -0,0 +1,145 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _GLINIT_H_ +#define _GLINIT_H_ + +// Common +#include "MathUtil.h" + +// VideoCommon +#include "VideoConfig.h" + +#include "pluginspecs_video.h" + +#ifdef _WIN32 + +#define GLEW_STATIC + +#include +#include + +#else // linux and apple basic definitions + +#if defined(USE_WX) && USE_WX +#include +#include "wx/wx.h" +#include "wx/glcanvas.h" + +#elif defined(HAVE_X11) && HAVE_X11 +#include +#include +#include +#include "Thread.h" + +#elif defined(__APPLE__) +#include +#include "cocoaGL.h" +#endif // end USE_WX + +#if defined(__APPLE__) +#include +#else +#include +#endif + +#endif // linux basic definitions + +#ifndef GL_DEPTH24_STENCIL8_EXT // allows FBOs to support stencils +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif + +#ifndef _WIN32 + +#include + +typedef struct { +#if defined(USE_WX) && USE_WX + wxGLCanvas *glCanvas; + wxPanel *panel; + wxGLContext *glCtxt; +#elif defined(__APPLE__) + NSWindow *cocoaWin; + NSOpenGLContext *cocoaCtx; +#elif defined(HAVE_X11) && HAVE_X11 + int screen; + Window win; + Window parent; + Display *dpy; + XVisualInfo *vi; + GLXContext ctx; + XSetWindowAttributes attr; + Common::Thread *xEventThread; + int x, y; +#endif + unsigned int width, height; +} GLWindow; + +extern GLWindow GLWin; + +#endif + +// Public OpenGL util + +//#if defined __APPLE__ || defined __linux__ || defined _WIN32 +//#include +//#include +//#define HAVE_CG 1 +//extern CGcontext g_cgcontext; +//extern CGprofile g_cgvProf, g_cgfProf; +//#endif + +namespace OGL +{ + +// Initialization / upkeep +bool OpenGL_Create(SVideoInitialize &_VideoInitialize, int _width, int _height); +void OpenGL_Shutdown(); +void OpenGL_Update(); +bool OpenGL_MakeCurrent(); +void OpenGL_SwapBuffers(); + +// Get status +u32 OpenGL_GetBackbufferWidth(); +u32 OpenGL_GetBackbufferHeight(); + +// Set things +void OpenGL_SetWindowText(const char *text); + +// Error reporting - use the convenient macros. +void OpenGL_ReportARBProgramError(); +GLuint OpenGL_ReportGLError(const char *function, const char *file, int line); +bool OpenGL_ReportFBOError(const char *function, const char *file, int line); + +#if defined(_DEBUG) || defined(DEBUGFAST) +#define GL_REPORT_ERROR() OpenGL_ReportGLError(__FUNCTION__, __FILE__, __LINE__) +#define GL_REPORT_ERRORD() OpenGL_ReportGLError(__FUNCTION__, __FILE__, __LINE__) +#define GL_REPORT_FBO_ERROR() OpenGL_ReportFBOError(__FUNCTION__, __FILE__, __LINE__) +#define GL_REPORT_PROGRAM_ERROR() OpenGL_ReportARBProgramError() +#else +#define GL_REPORT_ERROR() GL_NO_ERROR +#define GL_REPORT_ERRORD() (void)GL_NO_ERROR +#define GL_REPORT_FBO_ERROR() (void)true +#define GL_REPORT_PROGRAM_ERROR() (void)0 +#endif + +} + +#endif // _GLINIT_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_GLWindow.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_GLWindow.h new file mode 100644 index 0000000000..fd9a000309 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_GLWindow.h @@ -0,0 +1,178 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_GLWINDOW_H_ +#define _OGL_GLWINDOW_H_ + +#include + +#include "Common.h" + +#include "OGL_Globals.h" + +#include "pluginspecs_video.h" + +#ifdef _WIN32 +#define GLEW_STATIC + +#include +#include +#include +#include +#else +#include +#endif + +#if defined(__APPLE__) +#include +#else +#include +#endif + +namespace OGL +{ + +enum OGL_Props +{ + OGL_FULLSCREEN, + OGL_KEEPRATIO, + OGL_HIDECURSOR, + OGL_PROP_COUNT +}; + +struct res +{ + u32 x; + u32 y; +}; + +class GLWindow +{ +private: + + // TODO: what is xmax and ymax? do we need [xy]render? + u32 xWin, yWin; // Windows' size + int xOffset, yOffset; // Offset in window + float xMax, yMax; // ??? + u32 xRender, yRender; // Render area + + bool properties[OGL_PROP_COUNT]; + +protected: + + EventHandler* eventHandler; + res origRes, currFullRes, currWinRes; + static std::vector fullResolutions; + virtual void SetRender(u32 x, u32 y) + { + xRender = x; + yRender = y; + } + + static const std::vector& getFsResolutions() + { + return fullResolutions; + } + + static void addFSResolution(res fsr) + { + fullResolutions.push_back(fsr); + } +public: + + virtual void SwapBuffers() {}; + virtual void SetWindowText(const char *text) {}; + virtual bool PeekMessages() {return false;}; + virtual void Update() {}; + virtual bool MakeCurrent() {return false;}; + + virtual void updateDim() + { + if (GetProperty(OGL_FULLSCREEN)) + SetWinSize(currFullRes.x, currFullRes.y); + else + // Set the windowed resolution + SetWinSize(currWinRes.x, currWinRes.y); + + float FactorX = 640.0f / (float)GetXwin(); + float FactorY = 480.0f / (float)GetYwin(); + //float Max = (FactorX < FactorY) ? FactorX : FactorY; + + SetMax(1.0f / FactorX, 1.0f / FactorY); + SetOffset(0,0); + } + + void SetEventHandler(EventHandler *eh) { eventHandler = eh;} + bool GetProperty(OGL_Props prop) {return properties[prop];} + virtual bool SetProperty(OGL_Props prop, bool value) + {return properties[prop] = value;} + + u32 GetXrender() {return xRender;} + u32 GetYrender() {return yRender;} + + u32 GetXwin() {return xWin;} + u32 GetYwin() {return yWin;} + void SetWinSize(u32 x, u32 y) + { + xWin = x; + yWin = y; + } + + int GetYoff() {return yOffset;} + int GetXoff() {return xOffset;} + void SetOffset(int x, int y) + { + yOffset = y; + xOffset = x; + } + + void SetMax(float x, float y) + { + yMax = y; + xMax = x; + } + + float GetXmax() {return xMax;} + float GetYmax() {return yMax;} + + static bool valid() { return false;} + + GLWindow() + { + // Load defaults + sscanf(g_Config.iFSResolution, "%dx%d", + &currFullRes.x, &currFullRes.y); + + sscanf(g_Config.iInternalRes, "%dx%d", + &currWinRes.x, &currWinRes.y); + + SetProperty(OGL_FULLSCREEN, g_Config.bFullscreen); + // What does this do? + SetProperty(OGL_KEEPRATIO, g_Config.bKeepAR43); + SetProperty(OGL_HIDECURSOR, g_Config.bHideCursor); + + updateDim(); + } + + + // setResolution + // resolution iter +}; + +} + +#endif // _GLWINDOW_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_NativeVertexFormat.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_NativeVertexFormat.cpp new file mode 100644 index 0000000000..175b352ff4 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_NativeVertexFormat.cpp @@ -0,0 +1,310 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Common +#include "x64Emitter.h" +#include "ABI.h" +#include "MemoryUtil.h" + +// VideoCommon +#include "Profiler.h" +#include "VertexShaderGen.h" +#include "NativeVertexFormat.h" +#include "NativeVertexWriter.h" +#include "CPMemory.h" + +// OGL +#include "OGL_GLUtil.h" +#include "OGL_VertexManager.h" + +#include "../Main.h" + +namespace OGL +{ + +#define COMPILED_CODE_SIZE 4096 + +u32 s_prevcomponents; // previous state set +/* +#ifdef _WIN32 +#ifdef _M_IX86 +#define USE_JIT +#endif +#endif +*/ +// Note the use of CallCdeclFunction3I etc. +// This is a horrible hack that is necessary because in 64-bit mode, Opengl32.dll is based way, way above the 32-bit +// address space that is within reach of a CALL, and just doing &fn gives us these high uncallable addresses. So we +// want to grab the function pointers from the import table instead. + +// This problem does not apply to glew functions, only core opengl32 functions. + +// Here's some global state. We only use this to keep track of what we've sent to the OpenGL state +// machine. + +#ifdef USE_JIT +DECLARE_IMPORT(glNormalPointer); +DECLARE_IMPORT(glVertexPointer); +DECLARE_IMPORT(glColorPointer); +DECLARE_IMPORT(glTexCoordPointer); +#endif + +class GLVertexFormat : public NativeVertexFormat +{ + u8 *m_compiledCode; + PortableVertexDeclaration vtx_decl; + +public: + GLVertexFormat(); + ~GLVertexFormat(); + + virtual void Initialize(const PortableVertexDeclaration &_vtx_decl); + virtual void SetupVertexPointers() const; + virtual void EnableComponents(u32 components); +}; + +NativeVertexFormat* VertexManager::CreateNativeVertexFormat() +{ + return new GLVertexFormat; +} + +GLVertexFormat::GLVertexFormat() +{ +#ifdef USE_JIT + m_compiledCode = (u8 *)AllocateExecutableMemory(COMPILED_CODE_SIZE, false); + if (m_compiledCode) + memset(m_compiledCode, 0, COMPILED_CODE_SIZE); +#endif +} + +GLVertexFormat::~GLVertexFormat() +{ +#ifdef USE_JIT + FreeMemoryPages(m_compiledCode, COMPILED_CODE_SIZE); + m_compiledCode = 0; +#endif +} + +inline GLuint VarToGL(VarType t) +{ + static const GLuint lookup[5] = { + GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FLOAT + }; + return lookup[t]; +} + +void GLVertexFormat::Initialize(const PortableVertexDeclaration &_vtx_decl) +{ + vertex_stride = _vtx_decl.stride; + using namespace Gen; + + // We will not allow vertex components causing uneven strides. + if (_vtx_decl.stride & 3) + PanicAlert("Uneven vertex stride: %i", _vtx_decl.stride); + +#ifdef USE_JIT + Gen::XEmitter emit(m_compiledCode); + // Alright, we have our vertex declaration. Compile some crazy code to set it quickly using GL. + emit.ABI_EmitPrologue(6); + + emit.CallCdeclFunction4_I(glVertexPointer, 3, GL_FLOAT, _vtx_decl.stride, 0); + + if (_vtx_decl.num_normals >= 1) + { + emit.CallCdeclFunction3_I(glNormalPointer, VarToGL(_vtx_decl.normal_gl_type), _vtx_decl.stride, _vtx_decl.normal_offset[0]); + if (_vtx_decl.num_normals == 3) { + emit.CallCdeclFunction6((void *)glVertexAttribPointer, SHADER_NORM1_ATTRIB, _vtx_decl.normal_gl_size, VarToGL(_vtx_decl.normal_gl_type), GL_TRUE, _vtx_decl.stride, _vtx_decl.normal_offset[1]); + emit.CallCdeclFunction6((void *)glVertexAttribPointer, SHADER_NORM2_ATTRIB, _vtx_decl.normal_gl_size, VarToGL(_vtx_decl.normal_gl_type), GL_TRUE, _vtx_decl.stride, _vtx_decl.normal_offset[2]); + } + } + + for (int i = 0; i < 2; i++) + { + if (_vtx_decl.color_offset[i] != -1) + { + if (i == 0) + emit.CallCdeclFunction4_I(glColorPointer, 4, GL_UNSIGNED_BYTE, _vtx_decl.stride, _vtx_decl.color_offset[i]); + else + emit.CallCdeclFunction4((void *)glSecondaryColorPointer, 4, GL_UNSIGNED_BYTE, _vtx_decl.stride, _vtx_decl.color_offset[i]); + } + } + + for (int i = 0; i < 8; i++) + { + if (_vtx_decl.texcoord_offset[i] != -1) + { + int id = GL_TEXTURE0 + i; +#ifdef _M_X64 +#ifdef _MSC_VER + emit.MOV(32, R(RCX), Imm32(id)); +#else + emit.MOV(32, R(RDI), Imm32(id)); +#endif +#else + emit.ABI_AlignStack(1 * 4); + emit.PUSH(32, Imm32(id)); +#endif + emit.CALL((void *)glClientActiveTexture); +#ifndef _M_X64 +#ifdef _WIN32 + // don't inc stack on windows, stdcall +#else + emit.ABI_RestoreStack(1 * 4); +#endif +#endif + emit.CallCdeclFunction4_I( + glTexCoordPointer, _vtx_decl.texcoord_size[i], VarToGL(_vtx_decl.texcoord_gl_type[i]), + _vtx_decl.stride, _vtx_decl.texcoord_offset[i]); + } + } + + if (_vtx_decl.posmtx_offset != -1) + emit.CallCdeclFunction6((void *)glVertexAttribPointer, SHADER_POSMTX_ATTRIB, 4, GL_UNSIGNED_BYTE, GL_FALSE, _vtx_decl.stride, _vtx_decl.posmtx_offset); + + emit.ABI_EmitEpilogue(6); + + if (emit.GetCodePtr() - (u8*)m_compiledCode > COMPILED_CODE_SIZE) + Crash(); + +#endif + this->vtx_decl = _vtx_decl; +} + +void GLVertexFormat::SetupVertexPointers() const { + // Cast a pointer to compiled code to a pointer to a function taking no parameters, through a (void *) cast first to + // get around type checking errors, and call it. +#ifdef USE_JIT + ((void (*)())(void*)m_compiledCode)(); +#else + glVertexPointer(3, GL_FLOAT, vtx_decl.stride, ::VertexManager::s_pBaseBufferPointer); + if (vtx_decl.num_normals >= 1) { + glNormalPointer(VarToGL(vtx_decl.normal_gl_type), vtx_decl.stride, (void *)(::VertexManager::s_pBaseBufferPointer + vtx_decl.normal_offset[0])); + if (vtx_decl.num_normals == 3) { + glVertexAttribPointer(SHADER_NORM1_ATTRIB, vtx_decl.normal_gl_size, VarToGL(vtx_decl.normal_gl_type), GL_TRUE, vtx_decl.stride, (void *)(::VertexManager::s_pBaseBufferPointer + vtx_decl.normal_offset[1])); + glVertexAttribPointer(SHADER_NORM2_ATTRIB, vtx_decl.normal_gl_size, VarToGL(vtx_decl.normal_gl_type), GL_TRUE, vtx_decl.stride, (void *)(::VertexManager::s_pBaseBufferPointer + vtx_decl.normal_offset[2])); + } + } + + for (int i = 0; i < 2; i++) { + if (vtx_decl.color_offset[i] != -1) { + if (i == 0) + glColorPointer(4, GL_UNSIGNED_BYTE, vtx_decl.stride, (void *)(::VertexManager::s_pBaseBufferPointer + vtx_decl.color_offset[i])); + else { + glSecondaryColorPointer(4, GL_UNSIGNED_BYTE, vtx_decl.stride, (void *)(::VertexManager::s_pBaseBufferPointer + vtx_decl.color_offset[i])); + } + } + } + + for (int i = 0; i < 8; i++) { + if (vtx_decl.texcoord_offset[i] != -1) { + int id = GL_TEXTURE0 + i; + glClientActiveTexture(id); + glTexCoordPointer(vtx_decl.texcoord_size[i], VarToGL(vtx_decl.texcoord_gl_type[i]), + vtx_decl.stride, (void *)(::VertexManager::s_pBaseBufferPointer + vtx_decl.texcoord_offset[i])); + } + } + + if (vtx_decl.posmtx_offset != -1) { + glVertexAttribPointer(SHADER_POSMTX_ATTRIB, 4, GL_UNSIGNED_BYTE, GL_FALSE, vtx_decl.stride, (void *)(::VertexManager::s_pBaseBufferPointer + vtx_decl.posmtx_offset)); + } +#endif +} + +void GLVertexFormat::EnableComponents(u32 components) +{ + if (s_prevcomponents != components) + { + g_vertex_manager->Flush(); + + // matrices + if ((components & VB_HAS_POSMTXIDX) != (s_prevcomponents & VB_HAS_POSMTXIDX)) + { + if (components & VB_HAS_POSMTXIDX) + glEnableVertexAttribArray(SHADER_POSMTX_ATTRIB); + else + glDisableVertexAttribArray(SHADER_POSMTX_ATTRIB); + } + + // normals + if ((components & VB_HAS_NRM0) != (s_prevcomponents & VB_HAS_NRM0)) + { + if (components & VB_HAS_NRM0) + glEnableClientState(GL_NORMAL_ARRAY); + else + glDisableClientState(GL_NORMAL_ARRAY); + } + if ((components & VB_HAS_NRM1) != (s_prevcomponents & VB_HAS_NRM1)) + { + if (components & VB_HAS_NRM1) { + glEnableVertexAttribArray(SHADER_NORM1_ATTRIB); + glEnableVertexAttribArray(SHADER_NORM2_ATTRIB); + } + else { + glDisableVertexAttribArray(SHADER_NORM1_ATTRIB); + glDisableVertexAttribArray(SHADER_NORM2_ATTRIB); + } + } + + // color + for (int i = 0; i < 2; ++i) + { + if ((components & (VB_HAS_COL0 << i)) != (s_prevcomponents & (VB_HAS_COL0 << i))) + { + if (components & (VB_HAS_COL0 << i)) + glEnableClientState(i ? GL_SECONDARY_COLOR_ARRAY : GL_COLOR_ARRAY); + else + glDisableClientState(i ? GL_SECONDARY_COLOR_ARRAY : GL_COLOR_ARRAY); + } + } + + // tex + for (int i = 0; i < 8; ++i) + { + if (!g_ActiveConfig.bDisableTexturing) + { + if ((components & (VB_HAS_UV0 << i)) != (s_prevcomponents & (VB_HAS_UV0 << i))) + { + glClientActiveTexture(GL_TEXTURE0 + i); + if (components & (VB_HAS_UV0 << i)) + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + else + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + } + else + { + glClientActiveTexture(GL_TEXTURE0 + i); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + } + + // Disable Lighting + // TODO - Is this a good spot for this code? + if (g_ActiveConfig.bDisableLighting) + { + for (int i = 0; i < xfregs.nNumChans; i++) + { + xfregs.colChans[i].alpha.enablelighting = false; + xfregs.colChans[i].color.enablelighting = false; + } + } + + s_prevcomponents = components; + } +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PixelShaderCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PixelShaderCache.cpp new file mode 100644 index 0000000000..24295f305f --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PixelShaderCache.cpp @@ -0,0 +1,340 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +// Common +#include "Common.h" +#include "FileUtil.h" + +// VideoCommon +#include "Profiler.h" +#include "Statistics.h" +#include "VideoConfig.h" +#include "ImageWrite.h" +#include "PixelShaderManager.h" +#include "VertexShaderGen.h" + +// OGL +#include "OGL_Render.h" +#include "OGL_GLUtil.h" +#include "OGL_PixelShaderCache.h" + +#include "../Main.h" + +namespace OGL +{ + +static int s_nMaxPixelInstructions; +static GLuint s_ColorMatrixProgram = 0; +static GLuint s_DepthMatrixProgram = 0; +PixelShaderCache::PSCache PixelShaderCache::pshaders; +PIXELSHADERUID PixelShaderCache::s_curuid; +bool PixelShaderCache::s_displayCompileAlert; +GLuint PixelShaderCache::CurrentShader; +bool PixelShaderCache::ShaderEnabled; + +static FRAGMENTSHADER* pShaderLast = NULL; + + +void PixelShaderCache::SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + float f[4] = { f1, f2, f3, f4 }; + glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, const_number, f); + //SetPSConstant4fv(const_number, f); +} + +void PixelShaderCache::SetPSConstant4fv(unsigned int const_number, const float *f) +{ + glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, const_number, f); +} + +void PixelShaderCache::SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float *f) +{ + for (unsigned int i = 0; i < count; ++i, f+=4, ++const_number) + glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, const_number, f); + //SetPSConstant4fv(const_number, f); +} + +PixelShaderCache::PixelShaderCache() +{ + glEnable(GL_FRAGMENT_PROGRAM_ARB); + ShaderEnabled = true; + CurrentShader = 0; + GL_REPORT_ERRORD(); + + memset(&last_pixel_shader_uid, 0xFF, sizeof(last_pixel_shader_uid)); + + s_displayCompileAlert = true; + + glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB, (GLint *)&s_nMaxPixelInstructions); +#if CG_VERSION_NUM == 2100 + if (strstr((const char*)glGetString(GL_VENDOR), "ATI") != NULL) + { + s_nMaxPixelInstructions = 4096; + } +#endif + + int maxinst, maxattribs; + glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, (GLint *)&maxinst); + glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB, (GLint *)&maxattribs); + INFO_LOG(VIDEO, "pixel max_alu=%d, max_inst=%d, max_attrib=%d", s_nMaxPixelInstructions, maxinst, maxattribs); + + char pmatrixprog[1024]; + sprintf(pmatrixprog, "!!ARBfp1.0" + "TEMP R0;\n" + "TEMP R1;\n" + "TEX R0, fragment.texcoord[0], texture[0], RECT;\n" + "DP4 R1.w, R0, program.env[%d];\n" + "DP4 R1.z, R0, program.env[%d];\n" + "DP4 R1.x, R0, program.env[%d];\n" + "DP4 R1.y, R0, program.env[%d];\n" + "ADD result.color, R1, program.env[%d];\n" + "END\n", C_COLORMATRIX+3, C_COLORMATRIX+2, C_COLORMATRIX, C_COLORMATRIX+1, C_COLORMATRIX+4); + glGenProgramsARB(1, &s_ColorMatrixProgram); + SetCurrentShader(s_ColorMatrixProgram); + glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(pmatrixprog), pmatrixprog); + + GLenum err = GL_REPORT_ERROR(); + if (err != GL_NO_ERROR) { + ERROR_LOG(VIDEO, "Failed to create color matrix fragment program"); + glDeleteProgramsARB(1, &s_ColorMatrixProgram); + s_ColorMatrixProgram = 0; + } + + sprintf(pmatrixprog, "!!ARBfp1.0" + "TEMP R0;\n" + "TEMP R1;\n" + "TEMP R2;\n" + "PARAM K0 = { 65535.0, 255.0,1.0,16777215.0};\n" + "PARAM K1 = { 0.999999940395355224609375, 1.0000000596046483281045155587504,0.0,0.0};\n" + "TEX R2, fragment.texcoord[0], texture[0], RECT;\n" + "MUL R0, R2.x, K1.x;\n" + "MUL R0, R0.x, K0;\n" + "FRC R0, R0;\n" + "MUL R0, R0, K1.y;\n" + "DP4 R1.x, R0, program.env[%d];\n" + "DP4 R1.y, R0, program.env[%d];\n" + "DP4 R1.z, R0, program.env[%d];\n" + "DP4 R1.w, R0, program.env[%d];\n" + "ADD result.color, R1, program.env[%d];\n" + "END\n", C_COLORMATRIX, C_COLORMATRIX+1, C_COLORMATRIX+2, C_COLORMATRIX+3, C_COLORMATRIX+4); + glGenProgramsARB(1, &s_DepthMatrixProgram); + SetCurrentShader(s_DepthMatrixProgram); + glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(pmatrixprog), pmatrixprog); + + err = GL_REPORT_ERROR(); + if (err != GL_NO_ERROR) { + ERROR_LOG(VIDEO, "Failed to create depth matrix fragment program"); + glDeleteProgramsARB(1, &s_DepthMatrixProgram); + s_DepthMatrixProgram = 0; + } + +} + +PixelShaderCache::~PixelShaderCache() +{ + glDeleteProgramsARB(1, &s_ColorMatrixProgram); + s_ColorMatrixProgram = 0; + glDeleteProgramsARB(1, &s_DepthMatrixProgram); + s_DepthMatrixProgram = 0; + PSCache::iterator iter = pshaders.begin(); + for (; iter != pshaders.end(); iter++) + iter->second.Destroy(); + pshaders.clear(); +} + +GLuint PixelShaderCache::GetColorMatrixProgram() +{ + return s_ColorMatrixProgram; +} + +GLuint PixelShaderCache::GetDepthMatrixProgram() +{ + return s_DepthMatrixProgram; +} + +bool PixelShaderCache::SetShader(bool dstAlphaEnable) +{ + const FRAGMENTSHADER* const ps = GetShader(dstAlphaEnable); + if (ps) + { + SetCurrentShader(ps->glprogid); + return true; + } + else + return false; +} + +FRAGMENTSHADER* PixelShaderCache::GetShader(bool dstAlphaEnable) +{ + DVSTARTPROFILE(); + PIXELSHADERUID uid; + GetPixelShaderId(&uid, dstAlphaEnable ? 1 : 0); + if (uid == last_pixel_shader_uid && pshaders[uid].frameCount == frameCount) + { + return pShaderLast; + } + + memcpy(&last_pixel_shader_uid, &uid, sizeof(PIXELSHADERUID)); + + PSCache::iterator iter = pshaders.find(uid); + + if (iter != pshaders.end()) { + iter->second.frameCount = frameCount; + PSCacheEntry &entry = iter->second; + if (&entry.shader != pShaderLast) + { + pShaderLast = &entry.shader; + } + + return pShaderLast; + } + + //Make an entry in the table + PSCacheEntry& newentry = pshaders[uid]; + newentry.frameCount = frameCount; + pShaderLast = &newentry.shader; + const char *code = GeneratePixelShaderCode(dstAlphaEnable,API_OPENGL); + +#if defined(_DEBUG) || defined(DEBUGFAST) + if (g_ActiveConfig.iLog & CONF_SAVESHADERS && code) { + static int counter = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX), counter++); + + SaveData(szTemp, code); + } +#endif + + // printf("Compiling pixel shader. size = %i\n", strlen(code)); + if (!code || !CompilePixelShader(newentry.shader, code)) { + ERROR_LOG(VIDEO, "failed to create pixel shader"); + static int counter = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%sBADps_%04i.txt", File::GetUserPath(D_DUMP_IDX), counter++); + SaveData(szTemp, code); + return NULL; + } + + INCSTAT(stats.numPixelShadersCreated); + SETSTAT(stats.numPixelShadersAlive, pshaders.size()); + return pShaderLast; +} + +bool PixelShaderCache::CompilePixelShader(FRAGMENTSHADER& ps, const char* pstrprogram) +{ + GLenum err = GL_REPORT_ERROR(); + if (err != GL_NO_ERROR) + { + ERROR_LOG(VIDEO, "glError %08x before PS!", err); + } + +#if defined HAVE_CG && HAVE_CG + char stropt[128]; + sprintf(stropt, "MaxLocalParams=32,NumInstructionSlots=%d", s_nMaxPixelInstructions); + const char *opts[] = {"-profileopts", stropt, "-O2", "-q", NULL}; + CGprogram tempprog = cgCreateProgram(g_cgcontext, CG_SOURCE, pstrprogram, g_cgfProf, "main", opts); + + // handle errors + if (!cgIsProgram(tempprog)) { + cgDestroyProgram(tempprog); + ERROR_LOG(VIDEO, "Failed to compile ps %s:", cgGetLastListing(g_cgcontext)); + ERROR_LOG(VIDEO, pstrprogram); + return false; + } + + // handle warnings + if (cgGetError() != CG_NO_ERROR) + { + WARN_LOG(VIDEO, "Warnings on compile ps %s:", cgGetLastListing(g_cgcontext)); + WARN_LOG(VIDEO, pstrprogram); + } + + // This looks evil - we modify the program through the const char * we got from cgGetProgramString! + // It SHOULD not have any nasty side effects though - but you never know... + char *pcompiledprog = (char*)cgGetProgramString(tempprog, CG_COMPILED_PROGRAM); + char *plocal = strstr(pcompiledprog, "program.local"); + while (plocal != NULL) { + const char *penv = " program.env"; + memcpy(plocal, penv, 13); + plocal = strstr(plocal+13, "program.local"); + } + + glGenProgramsARB(1, &ps.glprogid); + SetCurrentShader(ps.glprogid); + glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(pcompiledprog), pcompiledprog); + + err = GL_REPORT_ERROR(); + if (err != GL_NO_ERROR) + { + GLint error_pos, native_limit; + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_pos); + glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &native_limit); + // Error occur + if (error_pos != -1) { + const char *program_error = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB); + char line[256]; + strncpy(line, (const char *)pcompiledprog + error_pos, 255); + line[255] = 0; + ERROR_LOG(VIDEO, "Error at %i: %s", error_pos, program_error); + ERROR_LOG(VIDEO, "Line dump: \n%s", line); + } else if (native_limit != -1) { + ERROR_LOG(VIDEO, "Hit limit? %i", native_limit); + // TODO + } + ERROR_LOG(VIDEO, pstrprogram); + ERROR_LOG(VIDEO, pcompiledprog); + } + + cgDestroyProgram(tempprog); +#endif + +#if defined(_DEBUG) || defined(DEBUGFAST) + ps.strprog = pstrprogram; +#endif + return true; +} + +//Disable Fragment programs and reset the selected Program +void PixelShaderCache::DisableShader() +{ + if(ShaderEnabled) + { + glDisable(GL_FRAGMENT_PROGRAM_ARB); + ShaderEnabled = false; + } +} + + +//bind a program if is diferent from the binded oone +void PixelShaderCache::SetCurrentShader(GLuint Shader) +{ + if(!ShaderEnabled) + { + glEnable(GL_FRAGMENT_PROGRAM_ARB); + ShaderEnabled = true; + } + if(CurrentShader != Shader) + { + if(Shader != 0) + CurrentShader = Shader; + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, CurrentShader); + } +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PixelShaderCache.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PixelShaderCache.h new file mode 100644 index 0000000000..08c532ca33 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PixelShaderCache.h @@ -0,0 +1,96 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_PIXELSHADERCACHE_H_ +#define _OGL_PIXELSHADERCACHE_H_ + +#include +#include + +// VideoCommon +#include "BPMemory.h" +#include "PixelShaderGen.h" + +#include "../PixelShaderCache.h" + +namespace OGL +{ + +struct FRAGMENTSHADER +{ + FRAGMENTSHADER() : glprogid(0) { } + void Destroy() + { + if (glprogid) + { + glDeleteProgramsARB(1, &glprogid); + glprogid = 0; + } + } + GLuint glprogid; // opengl program id +#if defined(_DEBUG) || defined(DEBUGFAST) + std::string strprog; +#endif +}; + +class PixelShaderCache : public ::PixelShaderCacheBase +{ + struct PSCacheEntry + { + FRAGMENTSHADER shader; + int frameCount; + + PSCacheEntry() : frameCount(0) {} + void Destroy() + { + shader.Destroy(); + } + }; + + typedef std::map PSCache; + + static PSCache pshaders; + static PIXELSHADERUID s_curuid; // the current pixel shader uid (progressively changed as memory is written) + static bool s_displayCompileAlert; + static GLuint CurrentShader; + static bool ShaderEnabled; + +public: + PixelShaderCache(); + ~PixelShaderCache(); + + static FRAGMENTSHADER* GetShader(bool dstAlphaEnable); + static bool CompilePixelShader(FRAGMENTSHADER& ps, const char* pstrprogram); + + static GLuint GetColorMatrixProgram(); + static GLuint GetDepthMatrixProgram(); + + bool SetShader(bool dstAlphaEnable); + + static void SetCurrentShader(GLuint Shader); + static void DisableShader(); + + void Clear() {} + + void SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4); + void SetPSConstant4fv(unsigned int const_number, const float *f); + void SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float *f); +}; + +} + +#endif // _PIXELSHADERCACHE_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PostProcessing.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PostProcessing.cpp new file mode 100644 index 0000000000..ac6661bf49 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PostProcessing.cpp @@ -0,0 +1,97 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Common +#include "FileUtil.h" + +// VideoCommon +#include "VideoCommon.h" +#include "VideoConfig.h" + +// OGL +#include "OGL_GLUtil.h" +#include "OGL_PostProcessing.h" +#include "OGL_PixelShaderCache.h" + +namespace OGL +{ + +namespace PostProcessing +{ + +static std::string s_currentShader; +static FRAGMENTSHADER s_shader; + +void Init() +{ + s_currentShader = ""; +} + +void Shutdown() +{ + s_shader.Destroy(); +} + +void ReloadShader() +{ + s_currentShader = ""; +} + +bool ApplyShader() +{ + if (s_currentShader != std::string(File::GetUserPath(D_SHADERS_IDX)) + g_ActiveConfig.sPostProcessingShader + ".txt") + { + // Set immediately to prevent endless recompiles on failure. + if (!g_ActiveConfig.sPostProcessingShader.empty()) + s_currentShader = std::string(File::GetUserPath(D_SHADERS_IDX)) + g_ActiveConfig.sPostProcessingShader + ".txt"; + else + s_currentShader.clear(); + + s_shader.Destroy(); + + if (!s_currentShader.empty()) + { + std::string code; + if (File::ReadFileToString(true, s_currentShader.c_str(), code)) + { + if (!PixelShaderCache::CompilePixelShader(s_shader, code.c_str())) + { + ERROR_LOG(VIDEO, "Failed to compile post-processing shader %s", s_currentShader.c_str()); + } + } + else + { + ERROR_LOG(VIDEO, "Failed to load post-processing shader %s - does not exist?", s_currentShader.c_str()); + } + } + } + + if (s_shader.glprogid != 0) + { + PixelShaderCache::SetCurrentShader(s_shader.glprogid); + return true; + } + else + { + PixelShaderCache::DisableShader(); + return false; + } +} + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PostProcessing.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PostProcessing.h new file mode 100644 index 0000000000..6bd680c4ad --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_PostProcessing.h @@ -0,0 +1,42 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_POSTPROCESSING_H_ +#define _OGL_POSTPROCESSING_H_ + +#include "VideoCommon.h" + +#include "OGL_GLUtil.h" + +namespace OGL +{ + +namespace PostProcessing +{ + +void Init(); +void Shutdown(); + +void ReloadShader(); +// Returns false if no shader was applied. +bool ApplyShader(); + +} // namespace + +} + +#endif // _POSTPROCESSING_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_RasterFont.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_RasterFont.cpp new file mode 100644 index 0000000000..d1d9bb7b42 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_RasterFont.cpp @@ -0,0 +1,226 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "OGL_GLUtil.h" +#include "OGL_RasterFont.h" +// globals + +namespace OGL +{ + +GLubyte rasters[][13] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00}, + {0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18}, + {0x00, 0x00, 0x0e, 0x1b, 0xdb, 0x6e, 0x30, 0x18, 0x0c, 0x76, 0xdb, 0xd8, 0x70}, + {0x00, 0x00, 0x7f, 0xc6, 0xcf, 0xd8, 0x70, 0x70, 0xd8, 0xcc, 0xcc, 0x6c, 0x38}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x0c, 0x0e}, + {0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c}, + {0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30}, + {0x00, 0x00, 0x00, 0x00, 0x99, 0x5a, 0x3c, 0xff, 0x3c, 0x5a, 0x99, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00}, + {0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03}, + {0x00, 0x00, 0x3c, 0x66, 0xc3, 0xe3, 0xf3, 0xdb, 0xcf, 0xc7, 0xc3, 0x66, 0x3c}, + {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18}, + {0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0xe7, 0x7e}, + {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0x07, 0x03, 0x03, 0xe7, 0x7e}, + {0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c}, + {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xff}, + {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, + {0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x03, 0x03, 0xff}, + {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e}, + {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x03, 0x7f, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e}, + {0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60}, + {0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x03, 0xc3, 0xc3, 0x7e}, + {0x00, 0x00, 0x3f, 0x60, 0xcf, 0xdb, 0xd3, 0xdd, 0xc3, 0x7e, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18}, + {0x00, 0x00, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, + {0x00, 0x00, 0x7e, 0xe7, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, + {0x00, 0x00, 0xfc, 0xce, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xce, 0xfc}, + {0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xff}, + {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xff}, + {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, + {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, + {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e}, + {0x00, 0x00, 0x7c, 0xee, 0xc6, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06}, + {0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc3}, + {0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}, + {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xff, 0xff, 0xe7, 0xc3}, + {0x00, 0x00, 0xc7, 0xc7, 0xcf, 0xcf, 0xdf, 0xdb, 0xfb, 0xf3, 0xf3, 0xe3, 0xe3}, + {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0x7e}, + {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, + {0x00, 0x00, 0x3f, 0x6e, 0xdf, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c}, + {0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, + {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0xe0, 0xc0, 0xc0, 0xe7, 0x7e}, + {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff}, + {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, + {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, + {0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, + {0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3}, + {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3}, + {0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x7e, 0x0c, 0x06, 0x03, 0x03, 0xff}, + {0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c}, + {0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60}, + {0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18}, + {0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x70}, + {0x00, 0x00, 0x7f, 0xc3, 0xc3, 0x7f, 0x03, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}, + {0x00, 0x00, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03}, + {0x00, 0x00, 0x7f, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x33, 0x1e}, + {0x7e, 0xc3, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0}, + {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00}, + {0x38, 0x6c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x00}, + {0x00, 0x00, 0xc6, 0xcc, 0xf8, 0xf0, 0xd8, 0xcc, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0}, + {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78}, + {0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xfe, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00}, + {0x03, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xfe, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xfe, 0x03, 0x03, 0x7e, 0xc0, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00}, + {0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x60, 0x60, 0x30, 0x18, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xff, 0x60, 0x30, 0x18, 0x0c, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x0f, 0x18, 0x18, 0x18, 0x38, 0xf0, 0x38, 0x18, 0x18, 0x18, 0x0f}, + {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0xf0, 0x18, 0x18, 0x18, 0x1c, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0xf0}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8f, 0xf1, 0x60, 0x00, 0x00, 0x00} +}; + +RasterFont::RasterFont() +{ + // set GL modes + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // create the raster font + fontOffset = glGenLists(128); + for (int i = 32; i < 127; i++) { + glNewList(i + fontOffset, GL_COMPILE); + glBitmap(8, 13, 0.0f, 2.0f, 10.0f, 0.0f, rasters[i - 32]); + glEndList(); + } + + temp_buffer = new char[TEMP_BUFFER_SIZE]; +} + +RasterFont::~RasterFont() +{ + glDeleteLists(fontOffset, 128); + delete [] temp_buffer; +} + +void RasterFont::printString(const char *s, double x, double y, double z) +{ + int length = (int)strlen(s); + if (!length) + return; + if (length >= TEMP_BUFFER_SIZE) + length = TEMP_BUFFER_SIZE - 1; + + // Sanitize string to avoid GL errors. + char *s2 = temp_buffer; + memcpy(s2, s, length); + s2[length] = 0; + for (int i = 0; i < length; i++) + if (s2[i] < 32 || s2[i] > 126) + s2[i] = '!'; + + // go to the right spot + glRasterPos3d(x, y, z); + GL_REPORT_ERRORD(); + + glPushAttrib (GL_LIST_BIT); + glListBase(fontOffset); + glCallLists((GLsizei)strlen(s2), GL_UNSIGNED_BYTE, (GLubyte *) s2); + GL_REPORT_ERRORD(); + glPopAttrib(); + GL_REPORT_ERRORD(); +} + +void RasterFont::printCenteredString(const char *s, double y, int screen_width, double z) +{ + int length = (int)strlen(s); + int x = (int)(screen_width/2.0 - (length/2.0)*char_width); + printString(s, x, y, z); +} + +void RasterFont::printMultilineText(const char *text, double start_x, double start_y, double z, int bbWidth, int bbHeight) +{ + double x = start_x; + double y = start_y; + char temp[1024]; + char *t = temp; + while (*text) + { + if (*text == '\n') + { + *t = 0; + printString(temp, x, y, z); + y -= char_height * 2.0f / bbHeight; + x = start_x; + t = temp; + } + else if (*text == '\r') + { + t = temp; + } + else if (*text == '\t') + { + //todo: add tabs every something like 4*char_width + *t = 0; + int cpos = (int)strlen(temp); + int newpos = (cpos + 4) & (~3); + printString(temp, x, y, z); + x = start_x + (char_width*newpos) * 2.0f / bbWidth; + t = temp; + *t++ = ' '; + } + else + *t++ = *text; + + text++; + } + + // ???? + if (t != text) + { + *t = 0; + printString(temp, x, y, z); + } +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_RasterFont.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_RasterFont.h new file mode 100644 index 0000000000..73d4dbf13e --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_RasterFont.h @@ -0,0 +1,51 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_RASTERFONT_H_ +#define _OGL_RASTERFONT_H_ + +namespace OGL +{ + +class RasterFont { +public: + RasterFont(); + ~RasterFont(void); + static int debug; + + // some useful constants + enum + { + char_width = 10, + char_height = 15, + }; + + // and the happy helper functions + void printString(const char *s, double x, double y, double z=0.0); + void printCenteredString(const char *s, double y, int screen_width, double z=0.0); + + void printMultilineText(const char *text, double x, double y, double z, int bbWidth, int bbHeight); + +private: + int fontOffset; + char *temp_buffer; + enum {TEMP_BUFFER_SIZE = 64 * 1024}; +}; + +} + +#endif // _RASTERFONT_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_Render.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_Render.cpp new file mode 100644 index 0000000000..0ece6694f3 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_Render.cpp @@ -0,0 +1,985 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef _WIN32 +//#include "OS/Win32.h" +//#include "AVIDump.h" + +#include +#endif + +#if defined(HAVE_WX) && HAVE_WX +#include +#endif + +// Common +#include "Thread.h" +#include "Atomic.h" +#include "FileUtil.h" +#include "CommonPaths.h" +#include "Timer.h" +#include "StringUtil.h" + +// VideoCommon +#include "VideoConfig.h" +#include "Profiler.h" +#include "Statistics.h" +#include "ImageWrite.h" +#include "OpcodeDecoding.h" +#include "BPStructs.h" +#include "VertexShaderGen.h" +#include "DLCache.h" +#include "PixelShaderManager.h" +#include "VertexShaderManager.h" +#include "VertexLoaderManager.h" +#include "VertexLoader.h" +#include "OnScreenDisplay.h" +#include "Fifo.h" + +// OGL +#include "OGL_GLUtil.h" +#include "OGL_TextureCache.h" +#include "OGL_RasterFont.h" +#include "OGL_PixelShaderCache.h" +#include "OGL_VertexShaderCache.h" +#include "OGL_PostProcessing.h" +#include "OGL_TextureConverter.h" +#include "OGL_FramebufferManager.h" +#include "OGL_XFB.h" +#include "OGL_Render.h" + +#include "../Main.h" + +namespace OGL +{ + +// Declarations and definitions +// ---------------------------- + +#if defined HAVE_CG && HAVE_CG +CGcontext g_cgcontext; +CGprofile g_cgvProf; +CGprofile g_cgfProf; +#endif + +RasterFont* s_pfont = NULL; + +static bool s_bLastFrameDumped = false; +#ifdef _WIN32 +static bool s_bAVIDumping = false; +#else +static FILE* f_pFrameDump; +#endif + +// 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA. +static int s_MSAASamples = 1; +static int s_MSAACoverageSamples = 0; + +bool s_bHaveFramebufferBlit = false; // export to FramebufferManager.cpp +static bool s_bHaveCoverageMSAA = false; + +// The custom resolution + +static bool s_skipSwap = false; + +// TODO: EmuWindow has these too, merge them +int OSDChoice = 0 , OSDTime = 0, OSDInternalW = 0, OSDInternalH = 0; + +#if defined(HAVE_WX) && HAVE_WX +// Screenshot thread struct +typedef struct +{ + int W, H; + std::string filename; + wxImage *img; +} ScrStrct; +#endif + +static const GLenum glSrcFactors[8] = +{ + GL_ZERO, + GL_ONE, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA +}; + +static const GLenum glDestFactors[8] = { + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA +}; + +static const GLenum glCmpFuncs[8] = { + GL_NEVER, + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS +}; + +static const GLenum glLogicOpCodes[16] = { + GL_CLEAR, + GL_AND, + GL_AND_REVERSE, + GL_COPY, + GL_AND_INVERTED, + GL_NOOP, + GL_XOR, + GL_OR, + GL_NOR, + GL_EQUIV, + GL_INVERT, + GL_OR_REVERSE, + GL_COPY_INVERTED, + GL_OR_INVERTED, + GL_NAND, + GL_SET +}; + +#if defined HAVE_CG && HAVE_CG +void HandleCgError(CGcontext ctx, CGerror err, void* appdata) +{ + DEBUG_LOG(VIDEO, "Cg error: %s", cgGetErrorString(err)); + const char* listing = cgGetLastListing(g_cgcontext); + if (listing != NULL) + DEBUG_LOG(VIDEO, " last listing: %s", listing); +} +#endif + +// Init functions +Renderer::Renderer() +{ + // hmm + if (!OpenGL_Create(g_VideoInitialize, 640, 480)) + { + g_VideoInitialize.pLog("Renderer::Create failed\n", TRUE); + return; + } + + OpenGL_MakeCurrent(); + + //if (!Renderer::Init()) { + // g_VideoInitialize.pLog("Renderer::Create failed\n", TRUE); + // PanicAlert("Can't create opengl renderer. You might be missing some required opengl extensions, check the logs for more info"); + // exit(1); + //} + + bool bSuccess = true; + s_MSAACoverageSamples = 0; + GLint numvertexattribs = 0; + + switch (g_ActiveConfig.iMultisampleMode) + { + case MULTISAMPLE_OFF: + s_MSAASamples = 1; + break; + + case MULTISAMPLE_2X: + s_MSAASamples = 2; + break; + + case MULTISAMPLE_4X: + s_MSAASamples = 4; + break; + + case MULTISAMPLE_8X: + s_MSAASamples = 8; + break; + + case MULTISAMPLE_CSAA_8X: + s_MSAASamples = 4; s_MSAACoverageSamples = 8; + break; + + case MULTISAMPLE_CSAA_8XQ: + s_MSAASamples = 8; s_MSAACoverageSamples = 8; + break; + + case MULTISAMPLE_CSAA_16X: + s_MSAASamples = 4; s_MSAACoverageSamples = 16; + break; + + case MULTISAMPLE_CSAA_16XQ: + s_MSAASamples = 8; s_MSAACoverageSamples = 16; + break; + + default: + s_MSAASamples = 1; + break; + } + +#if defined HAVE_CG && HAVE_CG + g_cgcontext = cgCreateContext(); + cgGetError(); + cgSetErrorHandler(HandleCgError, NULL); +#endif + + // Look for required extensions. + const char *const ptoken = (const char*)glGetString(GL_EXTENSIONS); + if (!ptoken) + { + PanicAlert("Your OpenGL Driver seems to be not working.\n" + "Please make sure your drivers are up-to-date and\n" + "that your video hardware is OpenGL 2.x compatible."); + //return false; + return; + } + + INFO_LOG(VIDEO, "Supported OpenGL Extensions:"); + INFO_LOG(VIDEO, ptoken); // write to the log file + INFO_LOG(VIDEO, ""); + + OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s", + glGetString(GL_VENDOR), + glGetString(GL_RENDERER), + glGetString(GL_VERSION)).c_str(), 5000); + + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &numvertexattribs); + if (numvertexattribs < 11) + { + ERROR_LOG(VIDEO, "GPU: OGL ERROR: Number of attributes %d not enough.\n" + "GPU: Does your video card support OpenGL 2.x?", + numvertexattribs); + bSuccess = false; + } + + // Init extension support. + if (glewInit() != GLEW_OK) + { + ERROR_LOG(VIDEO, "glewInit() failed! Does your video card support OpenGL 2.x?"); + //return false; + + return; + } + + if (!GLEW_EXT_framebuffer_object) + { + ERROR_LOG(VIDEO, "GPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets.\n" + "GPU: Does your video card support OpenGL 2.x?"); + bSuccess = false; + } + + if (!GLEW_EXT_secondary_color) + { + ERROR_LOG(VIDEO, "GPU: OGL ERROR: Need GL_EXT_secondary_color.\n" + "GPU: Does your video card support OpenGL 2.x?"); + bSuccess = false; + } + + s_bHaveFramebufferBlit = strstr(ptoken, "GL_EXT_framebuffer_blit") != NULL; + if (!s_bHaveFramebufferBlit) + { + // MSAA ain't gonna work. turn it off if enabled. + s_MSAASamples = 1; + } + + s_bHaveCoverageMSAA = strstr(ptoken, "GL_NV_framebuffer_multisample_coverage") != NULL; + if (!s_bHaveCoverageMSAA) + { + s_MSAACoverageSamples = 0; + } + + if (!bSuccess) + //return false; + return; + + // Handle VSync on/off +#if defined USE_WX && USE_WX + // TODO: FILL IN +#elif defined _WIN32 + if (WGLEW_EXT_swap_control) + wglSwapIntervalEXT(g_ActiveConfig.bVSync ? 1 : 0); + else + ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate)."); +#elif defined(HAVE_X11) && HAVE_X11 + if (glXSwapIntervalSGI) + glXSwapIntervalSGI(g_ActiveConfig.bVSync ? 1 : 0); + else + ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate)."); +#endif + + // check the max texture width and height + GLint max_texture_size; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&max_texture_size); + if (max_texture_size < 1024) + ERROR_LOG(VIDEO, "GL_MAX_TEXTURE_SIZE too small at %i - must be at least 1024.", + max_texture_size); + + if (GL_REPORT_ERROR() != GL_NO_ERROR) + bSuccess = false; + + if (glDrawBuffers == NULL && !GLEW_ARB_draw_buffers) + glDrawBuffers = glDrawBuffersARB; + + if (!GLEW_ARB_texture_non_power_of_two) + WARN_LOG(VIDEO, "ARB_texture_non_power_of_two not supported."); + + // Decide frambuffer size + FramebufferSize((int)OpenGL_GetBackbufferWidth(), (int)OpenGL_GetBackbufferHeight()); + + // Because of the fixed framebuffer size we need to disable the resolution + // options while running + g_Config.bRunning = true; + + if (GL_REPORT_ERROR() != GL_NO_ERROR) + bSuccess = false; + + // Initialize the FramebufferManager + g_framebuffer_manager = new FramebufferManager(s_backbuffer_width, + s_backbuffer_height, s_MSAASamples, s_MSAACoverageSamples); + + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + + if (GL_REPORT_ERROR() != GL_NO_ERROR) + bSuccess = false; + + s_pfont = new RasterFont(); + +#if defined HAVE_CG && HAVE_CG + // load the effect, find the best profiles (if any) + if (cgGLIsProfileSupported(CG_PROFILE_ARBVP1) != CG_TRUE) + { + ERROR_LOG(VIDEO, "arbvp1 not supported"); + return false; + } + + if (cgGLIsProfileSupported(CG_PROFILE_ARBFP1) != CG_TRUE) + { + ERROR_LOG(VIDEO, "arbfp1 not supported"); + return false; + } + + g_cgvProf = cgGLGetLatestProfile(CG_GL_VERTEX); + g_cgfProf = cgGLGetLatestProfile(CG_GL_FRAGMENT); +#if CG_VERSION_NUM == 2100 + // A bug was introduced in Cg2.1's handling of very large profile option values + // so this will not work on ATI. ATI returns MAXINT = 2147483647 (0x7fffffff) + // which is correct in OpenGL but Cg fails to handle it properly. As a result + // -1 is used by Cg resulting (signedness incorrect) and compilation fails. + if (strstr((const char*)glGetString(GL_VENDOR), "ATI") == NULL) +#endif + { + cgGLSetOptimalOptions(g_cgvProf); + cgGLSetOptimalOptions(g_cgfProf); + } +#endif // HAVE_CG + + int nenvvertparams, nenvfragparams, naddrregisters[2]; + glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, + GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, + (GLint *)&nenvvertparams); + glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, + GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, + (GLint *)&nenvfragparams); + glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, + GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, + (GLint *)&naddrregisters[0]); + glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, + GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, + (GLint *)&naddrregisters[1]); + DEBUG_LOG(VIDEO, "Max program env parameters: vert=%d, frag=%d", + nenvvertparams, nenvfragparams); + DEBUG_LOG(VIDEO, "Max program address register parameters: vert=%d, frag=%d", + naddrregisters[0], naddrregisters[1]); + + if (nenvvertparams < 238) + ERROR_LOG(VIDEO, "Not enough vertex shader environment constants!!"); + +#if defined HAVE_CG && HAVE_CG + INFO_LOG(VIDEO, "Max buffer sizes: %d %d", + cgGetProgramBufferMaxSize(g_cgvProf), + cgGetProgramBufferMaxSize(g_cgfProf)); +#ifndef _DEBUG + cgGLSetDebugMode(GL_FALSE); +#endif +#endif + + glStencilFunc(GL_ALWAYS, 0, 0); + glBlendFunc(GL_ONE, GL_ONE); + + glViewport(0, 0, GetTargetWidth(), GetTargetHeight()); // Reset The Current Viewport + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glShadeModel(GL_SMOOTH); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearDepth(1.0f); + glEnable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glDepthFunc(GL_LEQUAL); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment + + glDisable(GL_STENCIL_TEST); + glEnable(GL_SCISSOR_TEST); + + glScissor(0, 0, GetTargetWidth(), GetTargetHeight()); + glBlendColorEXT(0, 0, 0, 0.5f); + glClearDepth(1.0f); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // legacy multitexturing: select texture channel only. + glActiveTexture(GL_TEXTURE0); + glClientActiveTexture(GL_TEXTURE0); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + UpdateActiveConfig(); + //return GL_REPORT_ERROR() == GL_NO_ERROR && bSuccess; + + return; +} + +Renderer::~Renderer() +{ + g_Config.bRunning = false; + UpdateActiveConfig(); + delete s_pfont; + s_pfont = 0; + +#if defined HAVE_CG && HAVE_CG + if (g_cgcontext) + { + cgDestroyContext(g_cgcontext); + g_cgcontext = 0; + } +#endif + + delete g_framebuffer_manager; + +//#ifdef _WIN32 +// if(s_bAVIDumping) +// AVIDump::Stop(); +//#else +// if(f_pFrameDump != NULL) +// fclose(f_pFrameDump); +//#endif + + OpenGL_Shutdown(); +} + +// For the OSD menu's live resolution change +bool Renderer::Allow2x() +{ + if (GetFrameBufferWidth() >= 1280 && GetFrameBufferHeight() >= 960) + return true; + else + return false; +} + +bool Renderer::AllowCustom() +{ + //if (GetCustomWidth() <= GetFrameBufferWidth() && GetCustomHeight() <= GetFrameBufferHeight()) + // return true; + //else + // return false; + + return false; +} + +void Renderer::ResetAPIState() +{ + // Gets us to a reasonably sane state where it's possible to do things like + // image copies with textured quads, etc. + VertexShaderCache::DisableShader(); + PixelShaderCache::DisableShader(); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthMask(GL_FALSE); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +void UpdateViewport(); + +void Renderer::RestoreAPIState() +{ + // Gets us back into a more game-like state. + + UpdateViewport(); + + if (bpmem.genMode.cullmode > 0) glEnable(GL_CULL_FACE); + if (bpmem.zmode.testenable) glEnable(GL_DEPTH_TEST); + if (bpmem.zmode.updateenable) glDepthMask(GL_TRUE); + + glEnable(GL_SCISSOR_TEST); + SetScissorRect(); + SetColorMask(); + SetBlendMode(true); + + VertexShaderCache::SetCurrentShader(0); + PixelShaderCache::SetCurrentShader(0); +} + +void Renderer::SetColorMask() +{ + GLenum ColorMask = (bpmem.blendmode.colorupdate) ? GL_TRUE : GL_FALSE; + GLenum AlphaMask = (bpmem.blendmode.alphaupdate) ? GL_TRUE : GL_FALSE; + glColorMask(ColorMask, ColorMask, ColorMask, AlphaMask); +} + +void Renderer::SetBlendMode(bool forceUpdate) +{ + // blend mode bit mask + // 0 - blend enable + // 2 - reverse subtract enable (else add) + // 3-5 - srcRGB function + // 6-8 - dstRGB function + + u32 newval = bpmem.blendmode.subtract << 2; + + if (bpmem.blendmode.subtract) + newval |= 0x0049; // enable blending src 1 dst 1 + else if (bpmem.blendmode.blendenable) + { + newval |= 1; // enable blending + newval |= bpmem.blendmode.srcfactor << 3; + newval |= bpmem.blendmode.dstfactor << 6; + } + + u32 changes = forceUpdate ? 0xFFFFFFFF : newval ^ s_blendMode; + + if (changes & 1) + // blend enable change + (newval & 1) ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + + if (changes & 4) + // subtract enable change + glBlendEquation(newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD); + + if (changes & 0x1F8) + // blend RGB change + glBlendFunc(glSrcFactors[(newval >> 3) & 7], glDestFactors[(newval >> 6) & 7]); + + s_blendMode = newval; +} + +u32 Renderer::AccessEFB(EFBAccessType type, int x, int y) +{ + if(!g_ActiveConfig.bEFBAccessEnable) + return 0; + + // Get the rectangular target region covered by the EFB pixel. + EFBRectangle efbPixelRc; + efbPixelRc.left = x; + efbPixelRc.top = y; + efbPixelRc.right = x + 1; + efbPixelRc.bottom = y + 1; + + TargetRectangle targetPixelRc = ConvertEFBRectangle(efbPixelRc); + + // TODO (FIX) : currently, AA path is broken/offset and doesn't return the correct pixel + switch (type) + { + + case PEEK_Z: + { + if (s_MSAASamples > 1) + { + // Resolve our rectangle. + FramebufferManager::GetEFBDepthTexture(efbPixelRc); + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, FramebufferManager::GetResolvedFramebuffer()); + } + + // Sample from the center of the target region. + int srcX = (targetPixelRc.left + targetPixelRc.right) / 2; + int srcY = (targetPixelRc.top + targetPixelRc.bottom) / 2; + + u32 z = 0; + glReadPixels(srcX, srcY, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, &z); + GL_REPORT_ERRORD(); + + // Scale the 32-bit value returned by glReadPixels to a 24-bit + // value (GC uses a 24-bit Z-buffer). + // TODO: in RE0 this value is often off by one, which causes lighting to disappear + return z >> 8; + } + + case POKE_Z: + // TODO: Implement + break; + + case PEEK_COLOR: // GXPeekARGB + { + // Although it may sound strange, this really is A8R8G8B8 and not RGBA or 24-bit... + + // Tested in Killer 7, the first 8bits represent the alpha value which is used to + // determine if we're aiming at an enemy (0x80 / 0x88) or not (0x70) + // Wind Waker is also using it for the pictograph to determine the color of each pixel + + if (s_MSAASamples > 1) + { + // Resolve our rectangle. + FramebufferManager::GetEFBColorTexture(efbPixelRc); + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, FramebufferManager::GetResolvedFramebuffer()); + } + + // Sample from the center of the target region. + int srcX = (targetPixelRc.left + targetPixelRc.right) / 2; + int srcY = (targetPixelRc.top + targetPixelRc.bottom) / 2; + + // Read back pixel in BGRA format, then byteswap to get GameCube's ARGB Format. + u32 color = 0; + glReadPixels(srcX, srcY, 1, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &color); + GL_REPORT_ERRORD(); + + return color; + } + + case POKE_COLOR: + // TODO: Implement. One way is to draw a tiny pixel-sized rectangle at + // the exact location. Note: EFB pokes are susceptible to Z-buffering + // and perhaps blending. + //WARN_LOG(VIDEOINTERFACE, "This is probably some kind of software rendering"); + break; + + } + + return 0; +} + +// Function: This function handles the OpenGL glScissor() function +// ---------------------------- +// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg() +// case 0x52 > SetScissorRect() +// ---------------------------- +// bpmem.scissorTL.x, y = 342x342 +// bpmem.scissorBR.x, y = 981x821 +// GetTargetHeight() = the fixed ini file setting +// donkopunchstania - it appears scissorBR is the bottom right pixel inside the scissor box +// therefore the width and height are (scissorBR + 1) - scissorTL +bool Renderer::SetScissorRect() +{ + EFBRectangle rc; + if (g_renderer->SetScissorRect(rc)) + { + glScissor( + (int)(rc.left * EFBxScale), // x = 0 for example + (int)((EFB_HEIGHT - rc.bottom) * EFByScale), // y = 0 for example + (int)((rc.right - rc.left) * EFBxScale), // width = 640 for example + (int)((rc.bottom - rc.top) * EFByScale) // height = 480 for example + ); + return true; + } + else + { + glScissor(0, 0, GetTargetWidth(), GetTargetHeight()); + } + return false; +} + +void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, + bool alphaEnable, bool zEnable, u32 color, u32 z) +{ + // Update the view port for clearing the picture + TargetRectangle targetRc = ConvertEFBRectangle(rc); + glViewport(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight()); + glScissor(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight()); + + // Always set the scissor in case it was set by the game and has not been reset + + VertexShaderManager::SetViewportChanged(); + + GLbitfield bits = 0; + if (colorEnable) + { + bits |= GL_COLOR_BUFFER_BIT; + glClearColor( + ((color >> 16) & 0xFF) / 255.0f, + ((color >> 8) & 0xFF) / 255.0f, + (color & 0xFF) / 255.0f, + ((color >> 24) & 0xFF) / 255.0f + ); + } + if (zEnable) + { + bits |= GL_DEPTH_BUFFER_BIT; + glClearDepth((z & 0xFFFFFF) / float(0xFFFFFF)); + } + + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + glClear(bits); + SetScissorRect(); +} + +void Renderer::PrepareXFBCopy(const TargetRectangle &dst_rect) +{ + // Update GLViewPort + glViewport(dst_rect.left, dst_rect.bottom, dst_rect.GetWidth(), dst_rect.GetHeight()); + + GL_REPORT_ERRORD(); + + // Copy the framebuffer to screen. + + // Texture map s_xfbTexture onto the main buffer + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_RECTANGLE_ARB); + // Use linear filtering. + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void Renderer::Draw(const XFBSourceBase* xfbSource, const TargetRectangle& sourceRc, + const MathUtil::Rectangle& drawRc, const EFBRectangle& rc) +{ + // testing + //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + if (xfbSource) + { + // Texture map xfbSource->texture onto the main buffer + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, ((XFBSource*)xfbSource)->texture); + } + else + { + // Render to the real buffer now. + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, FramebufferManager::ResolveAndGetRenderTarget(rc)); + } + + // We must call ApplyShader here even if no post proc is selected - it takes + // care of disabling it in that case. It returns false in case of no post processing. + bool applyShader = PostProcessing::ApplyShader(); + if (applyShader) + { + glBegin(GL_QUADS); + glTexCoord2f(sourceRc.left, sourceRc.bottom); + glMultiTexCoord2fARB(GL_TEXTURE1, 0, 0); + glVertex2f(drawRc.left, drawRc.bottom); + + glTexCoord2f(sourceRc.left, sourceRc.top); + glMultiTexCoord2fARB(GL_TEXTURE1, 0, 1); + glVertex2f(drawRc.left, drawRc.top); + + glTexCoord2f(sourceRc.right, sourceRc.top); + glMultiTexCoord2fARB(GL_TEXTURE1, 1, 1); + glVertex2f(drawRc.right, drawRc.top); + + glTexCoord2f(sourceRc.right, sourceRc.bottom); + glMultiTexCoord2fARB(GL_TEXTURE1, 1, 0); + glVertex2f(drawRc.right, drawRc.bottom); + glEnd(); + PixelShaderCache::DisableShader(); + } + else + { + glBegin(GL_QUADS); + glTexCoord2f(sourceRc.left, sourceRc.bottom); + glVertex2f(drawRc.left, drawRc.bottom); + + glTexCoord2f(sourceRc.left, sourceRc.top); + glVertex2f(drawRc.left, drawRc.top); + + glTexCoord2f(sourceRc.right, sourceRc.top); + glVertex2f(drawRc.right, drawRc.top); + + glTexCoord2f(sourceRc.right, sourceRc.bottom); + glVertex2f(drawRc.right, drawRc.bottom); + glEnd(); + } + + GL_REPORT_ERRORD(); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + TextureCache::DisableStage(0); + + // TODO: silly place for this + // Wireframe + if (g_ActiveConfig.bWireFrame) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +} + +void Renderer::EndFrame() +{ + // Copy the rendered frame to the real window + OpenGL_SwapBuffers(); + + GL_REPORT_ERRORD(); + + // Clear framebuffer + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + GL_REPORT_ERRORD(); +} + +void Renderer::Present() +{ + // Render to the framebuffer. + FramebufferManager::SetFramebuffer(0); + + GL_REPORT_ERRORD(); +} + +bool Renderer::CheckForResize() +{ + // TODO: temp + + OpenGL_Update(); // just updates the render window position and the backbuffer size + return true; +} + +void Renderer::GetBackBufferSize(int* w, int* h) +{ + *w = (int)OpenGL_GetBackbufferWidth(); + *h = (int)OpenGL_GetBackbufferHeight(); +} + +void Renderer::RecreateFramebufferManger() +{ + delete g_framebuffer_manager; + g_framebuffer_manager = new FramebufferManager(s_backbuffer_width, + s_backbuffer_height, s_MSAASamples, s_MSAACoverageSamples); + + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); +} + +void Renderer::BeginFrame() +{ + // TODO: silly place for this + g_Config.iSaveTargetId = 0; + + bool last_copy_efb_to_Texture = g_ActiveConfig.bCopyEFBToTexture; + UpdateActiveConfig(); + if (last_copy_efb_to_Texture != g_ActiveConfig.bCopyEFBToTexture) + TextureCache::ClearRenderTargets(); +} + +// Called from VertexShaderManager +void Renderer::UpdateViewport() +{ + // reversed gxsetviewport(xorig, yorig, width, height, nearz, farz) + // [0] = width/2 + // [1] = height/2 + // [2] = 16777215 * (farz - nearz) + // [3] = xorig + width/2 + 342 + // [4] = yorig + height/2 + 342 + // [5] = 16777215 * farz + float scissorXOff = float(bpmem.scissorOffset.x) * 2.0f; // 342 + float scissorYOff = float(bpmem.scissorOffset.y) * 2.0f; // 342 + + // Stretch picture with increased internal resolution + int GLx = (int)ceil((xfregs.rawViewport[3] - xfregs.rawViewport[0] - scissorXOff) * + EFBxScale); + int GLy = (int)ceil( + (float(EFB_HEIGHT) - xfregs.rawViewport[4] + xfregs.rawViewport[1] + scissorYOff) * + EFByScale); + int GLWidth = (int)ceil(2.0f * xfregs.rawViewport[0] * EFBxScale); + int GLHeight = (int)ceil(-2.0f * xfregs.rawViewport[1] * EFByScale); + double GLNear = (xfregs.rawViewport[5] - xfregs.rawViewport[2]) / 16777216.0f; + double GLFar = xfregs.rawViewport[5] / 16777216.0f; + if(GLWidth < 0) + { + GLx += GLWidth; + GLWidth*=-1; + } + if(GLHeight < 0) + { + GLy += GLHeight; + GLHeight *= -1; + } + // Update the view port + glViewport(GLx, GLy, GLWidth, GLHeight); + glDepthRange(GLNear, GLFar); +} + +void Renderer::SetGenerationMode() +{ + // none, ccw, cw, ccw + if (bpmem.genMode.cullmode > 0) + { + glEnable(GL_CULL_FACE); + glFrontFace(bpmem.genMode.cullmode == 2 ? GL_CCW : GL_CW); + } + else + glDisable(GL_CULL_FACE); +} + +void Renderer::SetDepthMode() +{ + if (bpmem.zmode.testenable) + { + glEnable(GL_DEPTH_TEST); + glDepthMask(bpmem.zmode.updateenable ? GL_TRUE : GL_FALSE); + glDepthFunc(glCmpFuncs[bpmem.zmode.func]); + } + else + { + // if the test is disabled write is disabled too + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + } +} + +void Renderer::SetLogicOpMode() +{ + if (bpmem.blendmode.logicopenable && bpmem.blendmode.logicmode != 3) + { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(glLogicOpCodes[bpmem.blendmode.logicmode]); + } + else + glDisable(GL_COLOR_LOGIC_OP); +} + +void Renderer::SetDitherMode() +{ + if (bpmem.blendmode.dither) + glEnable(GL_DITHER); + else + glDisable(GL_DITHER); +} + +void Renderer::SetLineWidth() +{ + float fratio = xfregs.rawViewport[0] != 0 ? + ((float)GetTargetWidth() / EFB_WIDTH) : 1.0f; + if (bpmem.lineptwidth.linesize > 0) + // scale by ratio of widths + glLineWidth((float)bpmem.lineptwidth.linesize * fratio / 6.0f); + if (bpmem.lineptwidth.pointsize > 0) + glPointSize((float)bpmem.lineptwidth.pointsize * fratio / 6.0f); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_Render.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_Render.h new file mode 100644 index 0000000000..28d2a28f61 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_Render.h @@ -0,0 +1,61 @@ + +#ifndef _OGL_RENDER_H_ +#define _OGL_RENDER_H_ + +#include "MathUtil.h" + +#include "VideoCommon.h" +#include "Renderer.h" +#include "pluginspecs_video.h" + +namespace OGL +{ + +extern int OSDChoice; + +class Renderer : public ::RendererBase +{ +public: + Renderer(); + ~Renderer(); + + // What's the real difference between these? Too similar names. + void ResetAPIState(); + void RestoreAPIState(); + + void SetColorMask(); + void SetBlendMode(bool forceUpdate); + bool SetScissorRect(); + void SetGenerationMode(); + void SetDepthMode(); + void SetLogicOpMode(); + void SetDitherMode(); + void SetLineWidth(); + + // Live resolution change + bool Allow2x(); + bool AllowCustom(); + + u32 AccessEFB(EFBAccessType type, int x, int y); + + void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z); + void UpdateViewport(); + + // virtual funcs used by RendererBase::Swap + void PrepareXFBCopy(const TargetRectangle &dst_rect); + void Draw(const XFBSourceBase* xfbSource, const TargetRectangle& sourceRc, + const MathUtil::Rectangle& drawRc, const EFBRectangle& rc); + void EndFrame(); + void Present(); + bool CheckForResize(); + void GetBackBufferSize(int* w, int* h); + void RecreateFramebufferManger(); + void BeginFrame(); + +private: + +}; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureCache.cpp new file mode 100644 index 0000000000..3448607df1 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureCache.cpp @@ -0,0 +1,339 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include + +#ifdef _WIN32 +#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set +#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset +#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 +#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 +#include +#undef _interlockedbittestandset +#undef _interlockedbittestandreset +#undef _interlockedbittestandset64 +#undef _interlockedbittestandreset64 +#endif + +// Common +#include "CommonPaths.h" +#include "StringUtil.h" +#include "MemoryUtil.h" +#include "FileUtil.h" + +// VideoCommon +#include "VideoConfig.h" +#include "Hash.h" +#include "Statistics.h" +#include "Profiler.h" +#include "ImageWrite.h" +#include "BPStructs.h" +#include "TextureDecoder.h" +#include "PixelShaderManager.h" +#include "HiresTextures.h" +#include "VertexShaderManager.h" + +// OGL +#include "OGL_TextureCache.h" +#include "OGL_PixelShaderCache.h" +#include "OGL_TextureConverter.h" +#include "OGL_FramebufferManager.h" + +#include "../Main.h" + +namespace OGL +{ + +static u32 s_TempFramebuffer = 0; + +static const GLint c_MinLinearFilter[8] = { + GL_NEAREST, + GL_NEAREST_MIPMAP_NEAREST, + GL_NEAREST_MIPMAP_LINEAR, + GL_NEAREST, + GL_LINEAR, + GL_LINEAR_MIPMAP_NEAREST, + GL_LINEAR_MIPMAP_LINEAR, + GL_LINEAR, +}; + +static const GLint c_WrapSettings[4] = { + GL_CLAMP_TO_EDGE, + GL_REPEAT, + GL_MIRRORED_REPEAT, + GL_REPEAT, +}; + +TextureCache::TCacheEntry::~TCacheEntry() +{ + if (texture) + { + glDeleteTextures(1, &texture); + texture = 0; + } +} + +TextureCache::TCacheEntry::TCacheEntry() +{ + glGenTextures(1, &texture); + GL_REPORT_ERRORD(); +} + +void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level) +{ + if (bHaveMipMaps) + { + if (pcfmt != PC_TEX_FMT_DXT1) + { + if (expanded_width != (int)width) + glPixelStorei(GL_UNPACK_ROW_LENGTH, expanded_width); + + glTexImage2D(GL_TEXTURE_2D, level, gl_iformat, width, height, 0, gl_format, gl_type, TextureCache::temp); + + if (expanded_width != (int)width) + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } + else + { + // TODO: + //glCompressedTexImage2D(target, level, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, width, height, 0, expanded_width*expanded_height/2, temp); + } + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, width, height, 0, gl_format, gl_type, TextureCache::temp); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); + } +} + +void TextureCache::TCacheEntry::FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect) +{ + glBindTexture(GL_TEXTURE_2D, texture); + + // Make sure to resolve anything we need to read from. + const GLuint read_texture = bFromZBuffer ? FramebufferManager::ResolveAndGetDepthTarget(source_rect) : FramebufferManager::ResolveAndGetRenderTarget(source_rect); + + GL_REPORT_ERRORD(); + + if (s_TempFramebuffer == 0) + glGenFramebuffersEXT(1, (GLuint*)&s_TempFramebuffer); + + FramebufferManager::SetFramebuffer(s_TempFramebuffer); + // Bind texture to temporary framebuffer + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0); + GL_REPORT_FBO_ERROR(); + GL_REPORT_ERRORD(); + + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_RECTANGLE_ARB); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, read_texture); + + glViewport(0, 0, Scaledw, Scaledh); + + PixelShaderCache::SetCurrentShader(bFromZBuffer ? PixelShaderCache::GetDepthMatrixProgram() : PixelShaderCache::GetColorMatrixProgram()); + const float* const fConstAdd = colmat + 16; // fConstAdd is the last 4 floats of colmat + PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation + + GL_REPORT_ERRORD(); + + TargetRectangle targetSource = g_renderer->ConvertEFBRectangle(source_rect); + + glBegin(GL_QUADS); + glTexCoord2f((GLfloat)targetSource.left, (GLfloat)targetSource.bottom); glVertex2f(-1, 1); + glTexCoord2f((GLfloat)targetSource.left, (GLfloat)targetSource.top ); glVertex2f(-1, -1); + glTexCoord2f((GLfloat)targetSource.right, (GLfloat)targetSource.top ); glVertex2f( 1, -1); + glTexCoord2f((GLfloat)targetSource.right, (GLfloat)targetSource.bottom); glVertex2f( 1, 1); + glEnd(); + + GL_REPORT_ERRORD(); + + // Unbind texture from temporary framebuffer + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0); + + // TODO: these good here? + FramebufferManager::SetFramebuffer(0); + VertexShaderManager::SetViewportChanged(); + DisableStage(0); + + GL_REPORT_ERRORD(); + + // TODO: do this? + //glBindTexture(GL_TEXTURE_2D, 0); +} + +void TextureCache::TCacheEntry::Bind(unsigned int stage) +{ + glBindTexture(GL_TEXTURE_2D, texture); +} + +TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, + unsigned int height, unsigned int expanded_width, + unsigned int tex_levels, PC_TexFormat pcfmt) +{ + TCacheEntry &entry = *new TCacheEntry; + + int gl_format = 0; + int gl_iformat = 0; + int gl_type = 0; + + if (pcfmt != PC_TEX_FMT_DXT1) + { + switch (pcfmt) + { + default: + case PC_TEX_FMT_NONE: + PanicAlert("Invalid PC texture format %i", pcfmt); + case PC_TEX_FMT_BGRA32: + gl_format = GL_BGRA; + gl_iformat = 4; + gl_type = GL_UNSIGNED_BYTE; + break; + + case PC_TEX_FMT_RGBA32: + gl_format = GL_RGBA; + gl_iformat = 4; + gl_type = GL_UNSIGNED_BYTE; + break; + + case PC_TEX_FMT_I4_AS_I8: + gl_format = GL_LUMINANCE; + gl_iformat = GL_INTENSITY4; + gl_type = GL_UNSIGNED_BYTE; + break; + + case PC_TEX_FMT_IA4_AS_IA8: + gl_format = GL_LUMINANCE_ALPHA; + gl_iformat = GL_LUMINANCE4_ALPHA4; + gl_type = GL_UNSIGNED_BYTE; + break; + + case PC_TEX_FMT_I8: + gl_format = GL_LUMINANCE; + gl_iformat = GL_INTENSITY8; + gl_type = GL_UNSIGNED_BYTE; + break; + + case PC_TEX_FMT_IA8: + gl_format = GL_LUMINANCE_ALPHA; + gl_iformat = GL_LUMINANCE8_ALPHA8; + gl_type = GL_UNSIGNED_BYTE; + break; + + case PC_TEX_FMT_RGB565: + gl_format = GL_RGB; + gl_iformat = GL_RGB; + gl_type = GL_UNSIGNED_SHORT_5_6_5; + break; + } + } + + entry.gl_format = gl_format; + entry.gl_iformat = gl_iformat; + entry.gl_type = gl_type; + + // ok? + //Load(width, height, expanded_width, level); + + return &entry; +} + +TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(int scaled_tex_w, int scaled_tex_h) +{ + TCacheEntry &entry = *new TCacheEntry; + + entry.isDynamic = false; + + glBindTexture(GL_TEXTURE_2D, entry.texture); + GL_REPORT_ERRORD(); + + const GLenum gl_format = GL_RGBA, + gl_iformat = 4, + gl_type = GL_UNSIGNED_BYTE; + glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, scaled_tex_w, scaled_tex_w, 0, gl_format, gl_type, NULL); + + GL_REPORT_ERRORD(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (GL_REPORT_ERROR() != GL_NO_ERROR) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + GL_REPORT_ERRORD(); + } + + return &entry; +} + +void TextureCache::TCacheEntry::SetTextureParameters(TexMode0 &newmode, TexMode1 &newmode1) +{ + mode = newmode; + //mode1 = newmode1; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + (newmode.mag_filter || g_Config.bForceFiltering) ? GL_LINEAR : GL_NEAREST); + + if (bHaveMipMaps) + { + if (g_ActiveConfig.bForceFiltering && newmode.min_filter < 4) + mode.min_filter += 4; // take equivalent forced linear + + int filt = newmode.min_filter; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c_MinLinearFilter[filt & 7]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, newmode1.min_lod >> 4); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, newmode1.max_lod >> 4); + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, (newmode.lod_bias/32.0f)); + + } + else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + (g_ActiveConfig.bForceFiltering || newmode.min_filter >= 4) ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c_WrapSettings[newmode.wrap_s]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c_WrapSettings[newmode.wrap_t]); + + if (g_Config.iMaxAnisotropy >= 1) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)(1 << g_ActiveConfig.iMaxAnisotropy)); + +} + +TextureCache::~TextureCache() +{ + if (s_TempFramebuffer) + { + glDeleteFramebuffersEXT(1, (GLuint *)&s_TempFramebuffer); + s_TempFramebuffer = 0; + } +} + +void TextureCache::DisableStage(int stage) +{ + glActiveTexture(GL_TEXTURE0 + stage); + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_RECTANGLE_ARB); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureCache.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureCache.h new file mode 100644 index 0000000000..3e648e4031 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureCache.h @@ -0,0 +1,80 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_TEXTURECACHE_H_ +#define _OGL_TEXTURECACHE_H_ + +#include "VideoCommon.h" +#include "BPMemory.h" + +#include "OGL_GLUtil.h" + +#include "../TextureCache.h" + +namespace OGL +{ + +class TextureCache : public ::TextureCacheBase +{ +public: + struct TCacheEntry : TCacheEntryBase + { + GLuint texture; + + bool isDynamic; + bool bHaveMipMaps; + + PC_TexFormat pcfmt; + + int gl_format; + int gl_iformat; + int gl_type; + + TexMode0 mode; // current filter and clamp modes that texture is set to + TexMode1 mode1; // current filter and clamp modes that texture is set to + + void SetTextureParameters(TexMode0 &newmode, TexMode1 &newmode1); + + TCacheEntry(); + ~TCacheEntry(); + + void Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level); + + void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect); + + void Bind(unsigned int stage); + }; + + ~TextureCache(); + + TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt); + + TCacheEntryBase* CreateRenderTargetTexture(int scaled_tex_w, int scaled_tex_h); + + void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, unsigned int bScaleByHalf, const EFBRectangle &source); + + static void DisableStage(int stage); // sets active texture +}; + +//bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int height); + +} + +#endif // _TEXTUREMNGR_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureConverter.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureConverter.cpp new file mode 100644 index 0000000000..e95379aca0 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureConverter.cpp @@ -0,0 +1,486 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Fast image conversion using OpenGL shaders. +// This kind of stuff would be a LOT nicer with OpenCL. + +#include + +// Common +#include "FileUtil.h" + +// VideoCommon +#include "TextureConversionShader.h" +#include "VertexShaderManager.h" +#include "VideoConfig.h" +#include "ImageWrite.h" + +// OGL +#include "OGL_Render.h" +#include "OGL_TextureConverter.h" +#include "OGL_TextureCache.h" +#include "OGL_PixelShaderCache.h" +#include "OGL_FramebufferManager.h" + +#include "../Main.h" + +namespace OGL +{ + +namespace TextureConverter +{ + +static GLuint s_texConvFrameBuffer = 0; +static GLuint s_srcTexture = 0; // for decoding from RAM +static GLuint s_srcTextureWidth = 0; +static GLuint s_srcTextureHeight = 0; +static GLuint s_dstRenderBuffer = 0; // for encoding to RAM + +const int renderBufferWidth = 1024; +const int renderBufferHeight = 1024; + +static FRAGMENTSHADER s_rgbToYuyvProgram; +static FRAGMENTSHADER s_yuyvToRgbProgram; + +// Not all slots are taken - but who cares. +const u32 NUM_ENCODING_PROGRAMS = 64; +static FRAGMENTSHADER s_encodingPrograms[NUM_ENCODING_PROGRAMS]; + +void CreateRgbToYuyvProgram() +{ + // Output is BGRA because that is slightly faster than RGBA. + const char *FProgram = + "uniform samplerRECT samp0 : register(s0);\n" + "void main(\n" + " out float4 ocol0 : COLOR0,\n" + " in float2 uv0 : TEXCOORD0)\n" + "{\n" + " float2 uv1 = float2(uv0.x + 1.0f, uv0.y);\n" + " float3 c0 = texRECT(samp0, uv0).rgb;\n" + " float3 c1 = texRECT(samp0, uv1).rgb;\n" + " float3 y_const = float3(0.257f,0.504f,0.098f);\n" + " float3 u_const = float3(-0.148f,-0.291f,0.439f);\n" + " float3 v_const = float3(0.439f,-0.368f,-0.071f);\n" + " float4 const3 = float4(0.0625f,0.5f,0.0625f,0.5f);\n" + " float3 c01 = (c0 + c1) * 0.5f;\n" + " ocol0 = float4(dot(c1,y_const),dot(c01,u_const),dot(c0,y_const),dot(c01, v_const)) + const3;\n" + "}\n"; + + if (!PixelShaderCache::CompilePixelShader(s_rgbToYuyvProgram, FProgram)) { + ERROR_LOG(VIDEO, "Failed to create RGB to YUYV fragment program"); + } +} + +void CreateYuyvToRgbProgram() +{ + const char *FProgram = + "uniform samplerRECT samp0 : register(s0);\n" + "void main(\n" + " out float4 ocol0 : COLOR0,\n" + " in float2 uv0 : TEXCOORD0)\n" + "{\n" + " float4 c0 = texRECT(samp0, uv0).rgba;\n" + + " float f = step(0.5, frac(uv0.x));\n" + " float y = lerp(c0.b, c0.r, f);\n" + " float yComp = 1.164f * (y - 0.0625f);\n" + " float uComp = c0.g - 0.5f;\n" + " float vComp = c0.a - 0.5f;\n" + + " ocol0 = float4(yComp + (1.596f * vComp),\n" + " yComp - (0.813f * vComp) - (0.391f * uComp),\n" + " yComp + (2.018f * uComp),\n" + " 1.0f);\n" + "}\n"; + + if (!PixelShaderCache::CompilePixelShader(s_yuyvToRgbProgram, FProgram)) { + ERROR_LOG(VIDEO, "Failed to create YUYV to RGB fragment program"); + } +} + +FRAGMENTSHADER &GetOrCreateEncodingShader(u32 format) +{ + if (format > NUM_ENCODING_PROGRAMS) + { + PanicAlert("Unknown texture copy format: 0x%x\n", format); + return s_encodingPrograms[0]; + } + + if (s_encodingPrograms[format].glprogid == 0) + { + const char* shader = TextureConversionShader::GenerateEncodingShader(format,API_OPENGL); + +#if defined(_DEBUG) || defined(DEBUGFAST) + if (g_ActiveConfig.iLog & CONF_SAVESHADERS && shader) { + static int counter = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%senc_%04i.txt", File::GetUserPath(D_DUMP_IDX), counter++); + + SaveData(szTemp, shader); + } +#endif + + if (!PixelShaderCache::CompilePixelShader(s_encodingPrograms[format], shader)) { + ERROR_LOG(VIDEO, "Failed to create encoding fragment program"); + } + } + + return s_encodingPrograms[format]; +} + +void Init() +{ + glGenFramebuffersEXT(1, &s_texConvFrameBuffer); + + glGenRenderbuffersEXT(1, &s_dstRenderBuffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_dstRenderBuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA, renderBufferWidth, renderBufferHeight); + + glGenTextures(1, &s_srcTexture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_srcTexture); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + CreateRgbToYuyvProgram(); + CreateYuyvToRgbProgram(); +} + +void Shutdown() +{ + glDeleteTextures(1, &s_srcTexture); + glDeleteRenderbuffersEXT(1, &s_dstRenderBuffer); + glDeleteFramebuffersEXT(1, &s_texConvFrameBuffer); + + s_rgbToYuyvProgram.Destroy(); + s_yuyvToRgbProgram.Destroy(); + + for (unsigned int i = 0; i < NUM_ENCODING_PROGRAMS; i++) + s_encodingPrograms[i].Destroy(); + + s_srcTexture = 0; + s_dstRenderBuffer = 0; + s_texConvFrameBuffer = 0; +} + +void EncodeToRamUsingShader(FRAGMENTSHADER& shader, GLuint srcTexture, const TargetRectangle& sourceRc, + u8* destAddr, int dstWidth, int dstHeight, int readStride, bool toTexture, bool linearFilter) +{ + + + // switch to texture converter frame buffer + // attach render buffer as color destination + FramebufferManager::SetFramebuffer(s_texConvFrameBuffer); + + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_dstRenderBuffer); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, s_dstRenderBuffer); + GL_REPORT_ERRORD(); + + for (int i = 1; i < 8; ++i) + TextureCache::DisableStage(i); + + // set source texture + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_RECTANGLE_ARB); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, srcTexture); + + if (linearFilter) + { + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + GL_REPORT_ERRORD(); + + glViewport(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight); + + PixelShaderCache::SetCurrentShader(shader.glprogid); + + // Draw... + glBegin(GL_QUADS); + glTexCoord2f((float)sourceRc.left, (float)sourceRc.top); glVertex2f(-1,-1); + glTexCoord2f((float)sourceRc.left, (float)sourceRc.bottom); glVertex2f(-1,1); + glTexCoord2f((float)sourceRc.right, (float)sourceRc.bottom); glVertex2f(1,1); + glTexCoord2f((float)sourceRc.right, (float)sourceRc.top); glVertex2f(1,-1); + glEnd(); + GL_REPORT_ERRORD(); + + // .. and then readback the results. + // TODO: make this less slow. + + int writeStride = bpmem.copyMipMapStrideChannels * 32; + + if (writeStride != readStride && toTexture) + { + // writing to a texture of a different size + + int readHeight = readStride / dstWidth; + readHeight /= 4; // 4 bytes per pixel + + int readStart = 0; + int readLoops = dstHeight / readHeight; + for (int i = 0; i < readLoops; i++) + { + glReadPixels(0, readStart, (GLsizei)dstWidth, (GLsizei)readHeight, GL_BGRA, GL_UNSIGNED_BYTE, destAddr); + + readStart += readHeight; + destAddr += writeStride; + } + } + else + glReadPixels(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, destAddr); + + GL_REPORT_ERRORD(); + +} + +void EncodeToRam(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) +{ + u32 format = copyfmt; + + if (bFromZBuffer) + { + format |= _GX_TF_ZTF; + if (copyfmt == 11) + format = GX_TF_Z16; + else if (format < GX_TF_Z8 || format > GX_TF_Z24X8) + format |= _GX_TF_CTF; + } + else + if (copyfmt > GX_TF_RGBA8 || (copyfmt < GX_TF_RGB565 && !bIsIntensityFmt)) + format |= _GX_TF_CTF; + + FRAGMENTSHADER& texconv_shader = GetOrCreateEncodingShader(format); + if (texconv_shader.glprogid == 0) + return; + + u8 *dest_ptr = Memory_GetPtr(address); + + GLuint source_texture = bFromZBuffer ? FramebufferManager::ResolveAndGetDepthTarget(source) : FramebufferManager::ResolveAndGetRenderTarget(source); + + int width = (source.right - source.left) >> bScaleByHalf; + int height = (source.bottom - source.top) >> bScaleByHalf; + + int size_in_bytes = TexDecoder_GetTextureSizeInBytes(width, height, format); + + // Invalidate any existing texture covering this memory range. + // TODO - don't delete the texture if it already exists, just replace the contents. + TextureCache::InvalidateRange(address, size_in_bytes); + + u16 blkW = TexDecoder_GetBlockWidthInTexels(format) - 1; + u16 blkH = TexDecoder_GetBlockHeightInTexels(format) - 1; + u16 samples = TextureConversionShader::GetEncodedSampleCount(format); + + // only copy on cache line boundaries + // extra pixels are copied but not displayed in the resulting texture + s32 expandedWidth = (width + blkW) & (~blkW); + s32 expandedHeight = (height + blkH) & (~blkH); + + float MValueX = Renderer::GetTargetScaleX(); + float MValueY = Renderer::GetTargetScaleY(); + + float top = (EFB_HEIGHT - source.top - expandedHeight) * MValueY ; + + float sampleStride = bScaleByHalf?2.0f:1.0f; + + TextureConversionShader::SetShaderParameters((float)expandedWidth, + expandedHeight * MValueY, + source.left * MValueX, + top, + sampleStride * MValueX, + sampleStride * MValueY); + + TargetRectangle scaledSource; + scaledSource.top = 0; + scaledSource.bottom = expandedHeight; + scaledSource.left = 0; + scaledSource.right = expandedWidth / samples; + + + int cacheBytes = 32; + if ((format & 0x0f) == 6) + cacheBytes = 64; + + int readStride = (expandedWidth * cacheBytes) / TexDecoder_GetBlockWidthInTexels(format); + g_renderer->ResetAPIState(); + EncodeToRamUsingShader(texconv_shader, source_texture, scaledSource, dest_ptr, expandedWidth / samples, expandedHeight, readStride, true, bScaleByHalf > 0); + FramebufferManager::SetFramebuffer(0); + VertexShaderManager::SetViewportChanged(); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + TextureCache::DisableStage(0); + g_renderer->RestoreAPIState(); + GL_REPORT_ERRORD(); +} + + +u64 EncodeToRamFromTexture(u32 address,GLuint source_texture,float MValueX,float MValueY,bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) +{ + u32 format = copyfmt; + + if (bFromZBuffer) + { + format |= _GX_TF_ZTF; + if (copyfmt == 11) + format = GX_TF_Z16; + else if (format < GX_TF_Z8 || format > GX_TF_Z24X8) + format |= _GX_TF_CTF; + } + else + if (copyfmt > GX_TF_RGBA8 || (copyfmt < GX_TF_RGB565 && !bIsIntensityFmt)) + format |= _GX_TF_CTF; + + FRAGMENTSHADER& texconv_shader = GetOrCreateEncodingShader(format); + if (texconv_shader.glprogid == 0) + return 0; + + u8 *dest_ptr = Memory_GetPtr(address); + + int width = (source.right - source.left) >> bScaleByHalf; + int height = (source.bottom - source.top) >> bScaleByHalf; + + int size_in_bytes = TexDecoder_GetTextureSizeInBytes(width, height, format); + + u16 blkW = TexDecoder_GetBlockWidthInTexels(format) - 1; + u16 blkH = TexDecoder_GetBlockHeightInTexels(format) - 1; + u16 samples = TextureConversionShader::GetEncodedSampleCount(format); + + // only copy on cache line boundaries + // extra pixels are copied but not displayed in the resulting texture + s32 expandedWidth = (width + blkW) & (~blkW); + s32 expandedHeight = (height + blkH) & (~blkH); + + float sampleStride = bScaleByHalf?2.0f:1.0f; + float top = (EFB_HEIGHT - source.top - expandedHeight) * MValueY ; + TextureConversionShader::SetShaderParameters((float)expandedWidth, + expandedHeight * MValueY, + source.left * MValueX, + top, + sampleStride * MValueX, + sampleStride * MValueY); + + TargetRectangle scaledSource; + scaledSource.top = 0; + scaledSource.bottom = expandedHeight; + scaledSource.left = 0; + scaledSource.right = expandedWidth / samples; + + + int cacheBytes = 32; + if ((format & 0x0f) == 6) + cacheBytes = 64; + + int readStride = (expandedWidth * cacheBytes) / TexDecoder_GetBlockWidthInTexels(format); + + EncodeToRamUsingShader(texconv_shader, source_texture, scaledSource, dest_ptr, expandedWidth / samples, expandedHeight, readStride, true, bScaleByHalf > 0 && !bFromZBuffer); + g_texture_cache->MakeRangeDynamic(address,size_in_bytes); + return GetHash64(dest_ptr,size_in_bytes,g_ActiveConfig.iSafeTextureCache_ColorSamples); +} + +void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, + u8* destAddr, int dstWidth, int dstHeight) +{ + g_renderer->ResetAPIState(); + EncodeToRamUsingShader(s_rgbToYuyvProgram, srcTexture, sourceRc, destAddr, dstWidth / 2, dstHeight, 0, false, false); + FramebufferManager::SetFramebuffer(0); + VertexShaderManager::SetViewportChanged(); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + TextureCache::DisableStage(0); + g_renderer->RestoreAPIState(); + GL_REPORT_ERRORD(); +} + + +// Should be scale free. +void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture) +{ + u8* srcAddr = Memory_GetPtr(xfbAddr); + if (!srcAddr) + { + WARN_LOG(VIDEO, "Tried to decode from invalid memory address"); + return; + } + + g_renderer->ResetAPIState(); + + int srcFmtWidth = srcWidth / 2; + + // swich to texture converter frame buffer + // attach destTexture as color destination + FramebufferManager::SetFramebuffer(s_texConvFrameBuffer); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, destTexture); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, destTexture, 0); + + GL_REPORT_FBO_ERROR(); + + for (int i = 1; i < 8; ++i) + TextureCache::DisableStage(i); + + // activate source texture + // set srcAddr as data for source texture + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_RECTANGLE_ARB); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_srcTexture); + + // TODO: make this less slow. (How?) + if((GLsizei)s_srcTextureWidth == (GLsizei)srcFmtWidth && (GLsizei)s_srcTextureHeight == (GLsizei)srcHeight) + { + glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0,0,0,s_srcTextureWidth, s_srcTextureHeight, GL_BGRA, GL_UNSIGNED_BYTE, srcAddr); + } + else + { + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, (GLsizei)srcFmtWidth, (GLsizei)srcHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, srcAddr); + s_srcTextureWidth = (GLsizei)srcFmtWidth; + s_srcTextureHeight = (GLsizei)srcHeight; + } + + glViewport(0, 0, srcWidth, srcHeight); + + PixelShaderCache::SetCurrentShader(s_yuyvToRgbProgram.glprogid); + + GL_REPORT_ERRORD(); + + glBegin(GL_QUADS); + glTexCoord2f((float)srcFmtWidth, (float)srcHeight); + glVertex2f(1, -1); + glTexCoord2f((float)srcFmtWidth, 0); + glVertex2f(1, 1); + glTexCoord2f(0, 0); + glVertex2f(-1, 1); + glTexCoord2f(0, (float)srcHeight); + glVertex2f(-1, -1); + glEnd(); + + // reset state + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0); + TextureCache::DisableStage(0); + + VertexShaderManager::SetViewportChanged(); + + FramebufferManager::SetFramebuffer(0); + + g_renderer->RestoreAPIState(); + GL_REPORT_ERRORD(); +} + +} // namespace + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureConverter.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureConverter.h new file mode 100644 index 0000000000..650d50640f --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_TextureConverter.h @@ -0,0 +1,50 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_TEXTURECONVERTER_H_ +#define _OGL_TEXTURECONVERTER_H_ + +#include "VideoCommon.h" + +#include "OGL_GLUtil.h" + +namespace OGL +{ + +// Converts textures between formats +// TODO: support multiple texture formats +namespace TextureConverter +{ + +void Init(); +void Shutdown(); + +void EncodeToRam(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, + u32 copyfmt, int bScaleByHalf, const EFBRectangle& source); + +void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, + u8* destAddr, int dstWidth, int dstHeight); + +void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture); + +u64 EncodeToRamFromTexture(u32 address,GLuint source_texture,float MValueX,float MValueY, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source); + +} + +} + +#endif // _TEXTURECONVERTER_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexManager.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexManager.cpp new file mode 100644 index 0000000000..30397b89b2 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexManager.cpp @@ -0,0 +1,113 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +// Common +#include "MemoryUtil.h" +#include "FileUtil.h" + +// VideoCommon +#include "Fifo.h" +#include "VideoConfig.h" +#include "Statistics.h" +#include "Profiler.h" +#include "ImageWrite.h" +#include "BPMemory.h" +#include "IndexGenerator.h" +#include "OpcodeDecoding.h" +#include "PixelShaderManager.h" +#include "VertexShaderManager.h" +#include "VertexShaderGen.h" +#include "VertexLoader.h" + +// OGL +#include "OGL_Render.h" +#include "OGL_TextureCache.h" +#include "OGL_PixelShaderCache.h" +#include "OGL_VertexShaderCache.h" +#include "OGL_VertexManager.h" + +#include "../Main.h" + +namespace OGL +{ + +enum +{ + MAXVBUFFERSIZE = 0x1FFFF, + MAXIBUFFERSIZE = 0xFFFF, + MAXVBOBUFFERCOUNT = 0x8, +}; + +//static GLuint s_vboBuffers[MAXVBOBUFFERCOUNT] = {0}; +//static int s_nCurVBOIndex = 0; // current free buffer + +VertexManager::VertexManager() +{ + lastPrimitive = GX_DRAW_NONE; + + //s_nCurVBOIndex = 0; + //glGenBuffers(ARRAYSIZE(s_vboBuffers), s_vboBuffers); + glEnableClientState(GL_VERTEX_ARRAY); + g_nativeVertexFmt = NULL; + + GL_REPORT_ERRORD(); +} + +VertexManager::~VertexManager() +{ + //glDeleteBuffers(ARRAYSIZE(s_vboBuffers), s_vboBuffers); + //s_nCurVBOIndex = 0; +} + +void VertexManager::Draw(u32 /*stride*/, bool alphapass) +{ + if (alphapass) + { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + glDisable(GL_BLEND); + } + + if (IndexGenerator::GetNumTriangles() > 0) + { + glDrawElements(GL_TRIANGLES, IndexGenerator::GetTriangleindexLen(), GL_UNSIGNED_SHORT, TIBuffer); + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + if (IndexGenerator::GetNumLines() > 0) + { + glDrawElements(GL_LINES, IndexGenerator::GetLineindexLen(), GL_UNSIGNED_SHORT, LIBuffer); + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + if (IndexGenerator::GetNumPoints() > 0) + { + glDrawElements(GL_POINTS, IndexGenerator::GetPointindexLen(), GL_UNSIGNED_SHORT, PIBuffer); + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + + if (alphapass) + { + // restore color mask + g_renderer->SetColorMask(); + + if (bpmem.blendmode.blendenable || bpmem.blendmode.subtract) + glEnable(GL_BLEND); + } +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexManager.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexManager.h new file mode 100644 index 0000000000..0bd63a3e86 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexManager.h @@ -0,0 +1,44 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_VERTEXMANAGER_H_ +#define _OGL_VERTEXMANAGER_H_ + +#include "CPMemory.h" +#include "NativeVertexWriter.h" + +#include "../VertexManager.h" + +namespace OGL +{ + +// Handles the OpenGL details of drawing lots of vertices quickly. +// Other functionality is moving out. +class VertexManager : public ::VertexManagerBase +{ +public: + VertexManager(); + ~VertexManager(); + + void Draw(u32 stride, bool alphapass); + + NativeVertexFormat* CreateNativeVertexFormat(); +}; + +} + +#endif // _VERTEXMANAGER_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexShaderCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexShaderCache.cpp new file mode 100644 index 0000000000..e28283f2f3 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexShaderCache.cpp @@ -0,0 +1,255 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +// Common +#include "FileUtil.h" + +// VideoCommon +#include "Profiler.h" +#include "VideoConfig.h" +#include "Statistics.h" +#include "VertexShaderGen.h" +#include "VertexShaderManager.h" +#include "VertexLoader.h" +#include "XFMemory.h" +#include "ImageWrite.h" + +// OGL +#include "OGL_Render.h" +#include "OGL_VertexShaderCache.h" +#include "OGL_GLUtil.h" +#include "OGL_VertexManager.h" + +#include "../Main.h" + +namespace OGL +{ + +VertexShaderCache::VSCache VertexShaderCache::vshaders; +bool VertexShaderCache::s_displayCompileAlert; +GLuint VertexShaderCache::CurrentShader; +bool VertexShaderCache::ShaderEnabled; + +static VERTEXSHADER *pShaderLast = NULL; +static int s_nMaxVertexInstructions; + +void VertexShaderCache::SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + float f[4] = { f1, f2, f3, f4 }; + glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, const_number, f); +} + +void VertexShaderCache::SetVSConstant4fv(unsigned int const_number, const float *f) +{ + glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, const_number, f); +} + +void VertexShaderCache::SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float *f) +{ + for (unsigned int i = 0; i < count; i++,f+=4) + glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, const_number + i, f); +} + +void VertexShaderCache::SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float *f) +{ + for (unsigned int i = 0; i < count; i++) + { + float buf[4]; + buf[0] = *f++; + buf[1] = *f++; + buf[2] = *f++; + buf[3] = 0.f; + glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, const_number + i, buf); + } +} + + +VertexShaderCache::VertexShaderCache() +{ + glEnable(GL_VERTEX_PROGRAM_ARB); + ShaderEnabled = true; + CurrentShader = 0; + memset(&last_vertex_shader_uid, 0xFF, sizeof(last_vertex_shader_uid)); + + s_displayCompileAlert = true; + + glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, (GLint *)&s_nMaxVertexInstructions); +#if CG_VERSION_NUM == 2100 + if (strstr((const char*)glGetString(GL_VENDOR), "ATI") != NULL) + { + s_nMaxVertexInstructions = 4096; + } +#endif +} + +VertexShaderCache::~VertexShaderCache() +{ + for (VSCache::iterator iter = vshaders.begin(); iter != vshaders.end(); iter++) + iter->second.Destroy(); + vshaders.clear(); +} + +bool VertexShaderCache::SetShader(u32 components) +{ + const VERTEXSHADER* const vs = GetShader(components); + if (vs) + { + SetCurrentShader(vs->glprogid); + return true; + } + else + return false; +} + +VERTEXSHADER* VertexShaderCache::GetShader(u32 components) +{ + DVSTARTPROFILE(); + VERTEXSHADERUID uid; + GetVertexShaderId(&uid, components); + + if (uid == last_vertex_shader_uid && vshaders[uid].frameCount == frameCount) + { + return pShaderLast; + } + memcpy(&last_vertex_shader_uid, &uid, sizeof(VERTEXSHADERUID)); + + VSCache::iterator iter = vshaders.find(uid); + + if (iter != vshaders.end()) { + iter->second.frameCount = frameCount; + VSCacheEntry &entry = iter->second; + if (&entry.shader != pShaderLast) { + pShaderLast = &entry.shader; + } + + return pShaderLast; + } + + //Make an entry in the table + VSCacheEntry& entry = vshaders[uid]; + entry.frameCount = frameCount; + pShaderLast = &entry.shader; + const char *code = GenerateVertexShaderCode(components, API_OPENGL); + +#if defined(_DEBUG) || defined(DEBUGFAST) + if (g_ActiveConfig.iLog & CONF_SAVESHADERS && code) { + static int counter = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%svs_%04i.txt", File::GetUserPath(D_DUMP_IDX), counter++); + + SaveData(szTemp, code); + } +#endif + + if (!code || !VertexShaderCache::CompileVertexShader(entry.shader, code)) { + ERROR_LOG(VIDEO, "failed to create vertex shader"); + return NULL; + } + + INCSTAT(stats.numVertexShadersCreated); + SETSTAT(stats.numVertexShadersAlive, vshaders.size()); + return pShaderLast; +} + +bool VertexShaderCache::CompileVertexShader(VERTEXSHADER& vs, const char* pstrprogram) +{ + // Reset GL error before compiling shaders. Yeah, we need to investigate the causes of these. + GLenum err = GL_REPORT_ERROR(); + if (err != GL_NO_ERROR) + { + ERROR_LOG(VIDEO, "glError %08x before VS!", err); + } + +#if defined HAVE_CG && HAVE_CG + char stropt[64]; + sprintf(stropt, "MaxLocalParams=256,MaxInstructions=%d", s_nMaxVertexInstructions); + const char *opts[] = {"-profileopts", stropt, "-O2", "-q", NULL}; + CGprogram tempprog = cgCreateProgram(g_cgcontext, CG_SOURCE, pstrprogram, g_cgvProf, "main", opts); + if (!cgIsProgram(tempprog)) { + if (s_displayCompileAlert) { + PanicAlert("Failed to create vertex shader"); + s_displayCompileAlert = false; + } + cgDestroyProgram(tempprog); + ERROR_LOG(VIDEO, "Failed to load vs %s:", cgGetLastListing(g_cgcontext)); + ERROR_LOG(VIDEO, pstrprogram); + return false; + } + + if (cgGetError() != CG_NO_ERROR) + { + WARN_LOG(VIDEO, "Failed to load vs %s:", cgGetLastListing(g_cgcontext)); + WARN_LOG(VIDEO, pstrprogram); + } + + // This looks evil - we modify the program through the const char * we got from cgGetProgramString! + // It SHOULD not have any nasty side effects though - but you never know... + char *pcompiledprog = (char*)cgGetProgramString(tempprog, CG_COMPILED_PROGRAM); + char *plocal = strstr(pcompiledprog, "program.local"); + while (plocal != NULL) { + const char* penv = " program.env"; + memcpy(plocal, penv, 13); + plocal = strstr(plocal + 13, "program.local"); + } + glGenProgramsARB(1, &vs.glprogid); + SetCurrentShader(vs.glprogid); + + glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(pcompiledprog), pcompiledprog); + err = GL_REPORT_ERROR(); + if (err != GL_NO_ERROR) { + ERROR_LOG(VIDEO, pstrprogram); + ERROR_LOG(VIDEO, pcompiledprog); + } + + cgDestroyProgram(tempprog); +#endif + +#if defined(_DEBUG) || defined(DEBUGFAST) + vs.strprog = pstrprogram; +#endif + + return true; +} + +void VertexShaderCache::DisableShader() +{ + if (ShaderEnabled) + { + glDisable(GL_VERTEX_PROGRAM_ARB); + ShaderEnabled = false; + } +} + + +void VertexShaderCache::SetCurrentShader(GLuint Shader) +{ + if (!ShaderEnabled) + { + glEnable(GL_VERTEX_PROGRAM_ARB); + ShaderEnabled= true; + } + if (CurrentShader != Shader) + { + if(Shader != 0) + CurrentShader = Shader; + glBindProgramARB(GL_VERTEX_PROGRAM_ARB, CurrentShader); + } +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexShaderCache.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexShaderCache.h new file mode 100644 index 0000000000..013cf96424 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_VertexShaderCache.h @@ -0,0 +1,89 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_VERTEXSHADERCACHE_H_ +#define _OGL_VERTEXSHADERCACHE_H_ + +#include +#include + +// VideoCommon +#include "BPMemory.h" +#include "VertexShaderGen.h" + +// OGL +#include "OGL_GLUtil.h" + +#include "../VertexShaderCache.h" + +namespace OGL +{ + +struct VERTEXSHADER +{ + VERTEXSHADER() : glprogid(0) {} + GLuint glprogid; // opengl program id + +#if defined(_DEBUG) || defined(DEBUGFAST) + std::string strprog; +#endif +}; + +class VertexShaderCache : public ::VertexShaderCacheBase +{ + struct VSCacheEntry + { + VERTEXSHADER shader; + int frameCount; + VSCacheEntry() : frameCount(0) {} + void Destroy() { + // printf("Destroying vs %i\n", shader.glprogid); + glDeleteProgramsARB(1, &shader.glprogid); + shader.glprogid = 0; + } + }; + + typedef std::map VSCache; + + static VSCache vshaders; + + static bool s_displayCompileAlert; + + static GLuint CurrentShader; + static bool ShaderEnabled; + +public: + VertexShaderCache(); + ~VertexShaderCache(); + + bool SetShader(u32 components); + + static VERTEXSHADER* GetShader(u32 components); + static bool CompileVertexShader(VERTEXSHADER& ps, const char* pstrprogram); + + static void SetCurrentShader(GLuint Shader); + static void DisableShader(); + + void SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4); + void SetVSConstant4fv(unsigned int const_number, const float* f); + void SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float* f); + void SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float* f); +}; + +} + +#endif // _VERTEXSHADERCACHE_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_XFB.cpp b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_XFB.cpp new file mode 100644 index 0000000000..bc859d37b3 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_XFB.cpp @@ -0,0 +1,42 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + +// ---------------------------------------------------------------------------------------------------------- +// This file handles the External Frame Buffer (XFB). The XFB is a storage point when the picture is resized +// by the system to the correct display format for output to the TV. In most cases its function can be +// supplemented by the equivalent adjustments in glScissor and glViewport (or their DirectX equivalents). But +// for some homebrew games these functions are necessary because the homebrew game communicate directly with +// them. +// ---------------------------------------------------------------------------------------------------------- + +// OGL +#include "OGL_Render.h" +#include "OGL_TextureConverter.h" +#include "OGL_FramebufferManager.h" +#include "OGL_XFB.h" + +namespace OGL +{ + +void XFB_Write(u8 *xfb_in_ram, const EFBRectangle& sourceRc, u32 dstWd, u32 dstHt) +{ + TargetRectangle targetRc = Renderer::ConvertEFBRectangle(sourceRc); + TextureConverter::EncodeToRamYUYV(FramebufferManager::ResolveAndGetRenderTarget(sourceRc), targetRc, xfb_in_ram, dstWd, dstHt); +} + +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_XFB.h b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_XFB.h new file mode 100644 index 0000000000..a04d6f18af --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/OGL/OGL_XFB.h @@ -0,0 +1,31 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _OGL_XFB_H_ +#define _OGL_XFB_H_ + +#include "OGL_GLUtil.h" + +namespace OGL +{ + +// write the EFB to the XFB +void XFB_Write(u8 *xfb_in_ram, const EFBRectangle& sourceRc, u32 dstWd, u32 dstHt); + +} + +#endif // _XFB_H_ diff --git a/Source/Plugins/Plugin_VideoMerge/Src/PixelShaderCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/PixelShaderCache.cpp new file mode 100644 index 0000000000..207f131883 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/PixelShaderCache.cpp @@ -0,0 +1,20 @@ + +#include "PixelShaderManager.h" + +#include "Main.h" +#include "PixelShaderCache.h" + +void SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + g_pixel_shader_cache->SetPSConstant4f(const_number, f1, f2, f3, f4); +} + +void SetPSConstant4fv(unsigned int const_number, const float *f) +{ + g_pixel_shader_cache->SetPSConstant4fv(const_number, f); +} + +void SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float *f) +{ + g_pixel_shader_cache->SetMultiPSConstant4fv(const_number, count, f); +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/PixelShaderCache.h b/Source/Plugins/Plugin_VideoMerge/Src/PixelShaderCache.h new file mode 100644 index 0000000000..4dcc510cfb --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/PixelShaderCache.h @@ -0,0 +1,20 @@ + +#ifndef _PIXELSHADERCACHE_H_ +#define _PIXELSHADERCACHE_H_ + +class PixelShaderCacheBase +{ +public: + virtual ~PixelShaderCacheBase() {} + + virtual bool SetShader(bool dstAlphaEnable) = 0; + + virtual void SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) = 0; + virtual void SetPSConstant4fv(unsigned int const_number, const float *f) = 0; + virtual void SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float *f) = 0; + +protected: + +}; + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/Renderer.cpp b/Source/Plugins/Plugin_VideoMerge/Src/Renderer.cpp new file mode 100644 index 0000000000..1779e0be53 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/Renderer.cpp @@ -0,0 +1,390 @@ + +#include + +#include "Common.h" +#include "Timer.h" + +#include "Render.h" +#include "BPMemory.h" +#include "Atomic.h" +#include "VideoConfig.h" +#include "FramebufferManager.h" + +#include "Fifo.h" +#include "VertexShaderManager.h" +#include "DLCache.h" +#include "OnScreenDisplay.h" +#include "Statistics.h" + +#include "Renderer.h" + +#include "Main.h" + +int RendererBase::s_target_width; +int RendererBase::s_target_height; + +int RendererBase::s_Fulltarget_width; +int RendererBase::s_Fulltarget_height; + +int RendererBase::s_backbuffer_width; +int RendererBase::s_backbuffer_height; + +int RendererBase::s_XFB_width; +int RendererBase::s_XFB_height; + +float RendererBase::xScale; +float RendererBase::yScale; + +int RendererBase::s_fps; + +u32 RendererBase::s_blendMode; +bool RendererBase::XFBWrited; + +float RendererBase::EFBxScale; +float RendererBase::EFByScale; + +volatile u32 RendererBase::s_swapRequested; + +//void VideoConfig::UpdateProjectionHack() +//{ +// return; +// //::UpdateProjectionHack(g_Config.iPhackvalue); +//} + +RendererBase::RendererBase() +{ + UpdateActiveConfig(); + s_blendMode = 0; + + s_XFB_width = MAX_XFB_WIDTH; + s_XFB_height = MAX_XFB_HEIGHT; +} + +// can maybe reuse this func in Renderer::Swap to eliminate redundant code +void RendererBase::FramebufferSize(int w, int h) +{ + TargetRectangle dst_rect; + ComputeDrawRectangle(w, h, false, &dst_rect); + + xScale = (float)(dst_rect.right - dst_rect.left) / (float)s_XFB_width; + yScale = (float)(dst_rect.bottom - dst_rect.top) / (float)s_XFB_height; + + // TODO: why these, prolly can remove them + const int s_LastAA = g_ActiveConfig.iMultisampleMode; + const int s_LastEFBScale = g_ActiveConfig.iEFBScale; + + switch (s_LastEFBScale) + { + case 0: + EFBxScale = xScale; + EFByScale = yScale; + break; + + case 1: + EFBxScale = ceilf(xScale); + EFByScale = ceilf(yScale); + break; + + default: + EFByScale = EFBxScale = (g_ActiveConfig.iEFBScale - 1); + break; + }; + + const float SupersampleCoeficient = s_LastAA + 1; + EFBxScale *= SupersampleCoeficient; + EFByScale *= SupersampleCoeficient; + + s_target_width = (int)(EFB_WIDTH * EFBxScale); + s_target_height = (int)(EFB_HEIGHT * EFByScale); + + // TODO: set anything else? + s_Fulltarget_width = s_target_width; + s_Fulltarget_height = s_target_height; +} + +void UpdateViewport() +{ + g_renderer->UpdateViewport(); +} + +void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + RendererBase::RenderToXFB(xfbAddr, fbWidth, fbHeight, sourceRc); +} + +// whats this for? +bool IsD3D() +{ + //PanicAlert("IsD3D!"); + // TODO: temporary + return true; +} + +void Renderer::RenderText(const char *pstr, int left, int top, u32 color) +{ + +} + +void RendererBase::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + if (!fbWidth || !fbHeight) + return; + + VideoFifo_CheckEFBAccess(); + VideoFifo_CheckSwapRequestAt(xfbAddr, fbWidth, fbHeight); + XFBWrited = true; + + // XXX: Without the VI, how would we know what kind of field this is? So + // just use progressive. + + if (g_ActiveConfig.bUseXFB) + { + g_framebuffer_manager->CopyToXFB(xfbAddr, fbWidth, fbHeight, sourceRc); + } + else + { + g_renderer->Swap(xfbAddr, FIELD_PROGRESSIVE, fbWidth, fbHeight, sourceRc); + Common::AtomicStoreRelease(s_swapRequested, FALSE); + } +} + +TargetRectangle RendererBase::ConvertEFBRectangle(const EFBRectangle& rc) +{ + int Xstride = (s_Fulltarget_width - s_target_width) / 2; + int Ystride = (s_Fulltarget_height - s_target_height) / 2; + TargetRectangle result; + result.left = (int)(rc.left * EFBxScale) + Xstride; + result.top = (int)(rc.top * EFByScale) + Ystride; + result.right = (int)(rc.right * EFBxScale) + Xstride; + result.bottom = (int)(rc.bottom * EFByScale) + Ystride; + return result; +} + +bool RendererBase::SetScissorRect(EFBRectangle &rc) +{ + int xoff = bpmem.scissorOffset.x * 2 - 342; + int yoff = bpmem.scissorOffset.y * 2 - 342; + + rc.left = bpmem.scissorTL.x - xoff - 342; + rc.top = bpmem.scissorTL.y - yoff - 342; + rc.right = bpmem.scissorBR.x - xoff - 341; + rc.bottom = bpmem.scissorBR.y - yoff - 341; + + int Xstride = (s_Fulltarget_width - s_target_width) / 2; + int Ystride = (s_Fulltarget_height - s_target_height) / 2; + + rc.left = (int)(rc.left * EFBxScale); + rc.top = (int)(rc.top * EFByScale); + rc.right = (int)(rc.right * EFBxScale); + rc.bottom = (int)(rc.bottom * EFByScale); + + if (rc.left < 0) rc.left = 0; + if (rc.right < 0) rc.right = 0; + if (rc.left > s_target_width) rc.left = s_target_width; + if (rc.right > s_target_width) rc.right = s_target_width; + if (rc.top < 0) rc.top = 0; + if (rc.bottom < 0) rc.bottom = 0; + if (rc.top > s_target_height) rc.top = s_target_height; + if (rc.bottom > s_target_height) rc.bottom = s_target_height; + + rc.left += Xstride; + rc.top += Ystride; + rc.right += Xstride; + rc.bottom += Ystride; + + if (rc.left > rc.right) + { + int temp = rc.right; + rc.right = rc.left; + rc.left = temp; + } + if (rc.top > rc.bottom) + { + int temp = rc.bottom; + rc.bottom = rc.top; + rc.top = temp; + } + + return (rc.right >= rc.left && rc.bottom >= rc.top); +} + +void RendererBase::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight, const EFBRectangle& rc) +{ + if (g_bSkipCurrentFrame || (!XFBWrited && !g_ActiveConfig.bUseRealXFB) || !fbWidth || !fbHeight) + { + g_VideoInitialize.pCopiedToXFB(false); + return; + } + + // this function is called after the XFB field is changed, not after + // EFB is copied to XFB. In this way, flickering is reduced in games + // and seems to also give more FPS in ZTP + + if (field == FIELD_LOWER) + xfbAddr -= fbWidth * 2; + u32 xfbCount = 0; + const XFBSourceBase** xfbSourceList = g_framebuffer_manager->GetXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount); + + if ((!xfbSourceList || xfbCount == 0) && g_ActiveConfig.bUseXFB && !g_ActiveConfig.bUseRealXFB) + { + g_VideoInitialize.pCopiedToXFB(false); + return; + } + + g_renderer->ResetAPIState(); + + // prepare copying the XFBs to our backbuffer + TargetRectangle dst_rect; + ComputeDrawRectangle(s_backbuffer_width, s_backbuffer_height, false, &dst_rect); + + g_renderer->PrepareXFBCopy(dst_rect); + + if (g_ActiveConfig.bUseXFB) + { + const XFBSourceBase* xfbSource; + + // draw each xfb source + for (u32 i = 0; i < xfbCount; ++i) + { + xfbSource = xfbSourceList[i]; + TargetRectangle sourceRc; + + //if (g_ActiveConfig.bAutoScale) + //{ + // sourceRc = xfbSource->sourceRc; + //} + //else + //{ + sourceRc.left = 0; + sourceRc.top = 0; + sourceRc.right = xfbSource->texWidth; + sourceRc.bottom = xfbSource->texHeight; + //} + + MathUtil::Rectangle drawRc; + + if (g_ActiveConfig.bUseXFB && !g_ActiveConfig.bUseRealXFB) + { + // use virtual xfb with offset + int xfbHeight = xfbSource->srcHeight; + int xfbWidth = xfbSource->srcWidth; + int hOffset = ((s32)xfbSource->srcAddr - (s32)xfbAddr) / ((s32)fbWidth * 2); + + drawRc.bottom = 1.0f - 2.0f * ((hOffset) / (float)fbHeight); + drawRc.top = 1.0f - 2.0f * ((hOffset + xfbHeight) / (float)fbHeight); + drawRc.left = -(xfbWidth / (float)fbWidth); + drawRc.right = (xfbWidth / (float)fbWidth); + + if (!g_ActiveConfig.bAutoScale) + { + // scale draw area for a 1 to 1 pixel mapping with the draw target + float vScale = (float)fbHeight / (float)s_backbuffer_height; + float hScale = (float)fbWidth / (float)s_backbuffer_width; + + drawRc.top *= vScale; + drawRc.bottom *= vScale; + drawRc.left *= hScale; + drawRc.right *= hScale; + } + } + else + { + drawRc.top = -1; + drawRc.bottom = 1; + drawRc.left = -1; + drawRc.right = 1; + } + + g_renderer->Draw(xfbSource, sourceRc, drawRc, rc); + } + } + else + { + // TODO: organize drawRc stuff + MathUtil::Rectangle drawRc; + drawRc.top = -1; + drawRc.bottom = 1; + drawRc.left = -1; + drawRc.right = 1; + + const TargetRectangle targetRc = ConvertEFBRectangle(rc); + g_renderer->Draw(NULL, targetRc, drawRc, rc); + } + + // done with drawing the game stuff, good moment to save a screenshot + // TODO: screenshot code + // + + // finally present some information + // TODO: debug text, fps, etc... + // + + OSD::DrawMessages(); + + g_renderer->EndFrame(); + + ++frameCount; + + DLCache::ProgressiveCleanup(); + g_texture_cache->Cleanup(); + + // check for configuration changes + const int last_efbscale = g_ActiveConfig.iEFBScale; + //const int last_aa = g_ActiveConfig.iMultisampleMode; + + UpdateActiveConfig(); + + + bool window_resized = g_renderer->CheckForResize(); + + bool xfbchanged = false; + if (s_XFB_width != fbWidth || s_XFB_height != fbHeight) + { + xfbchanged = true; + s_XFB_width = fbWidth; + s_XFB_height = fbHeight; + if (s_XFB_width < 1) s_XFB_width = MAX_XFB_WIDTH; + if (s_XFB_width > MAX_XFB_WIDTH) s_XFB_width = MAX_XFB_WIDTH; + if (s_XFB_height < 1) s_XFB_height = MAX_XFB_HEIGHT; + if (s_XFB_height > MAX_XFB_HEIGHT) s_XFB_height = MAX_XFB_HEIGHT; + } + + // update FPS counter + // TODO: make this better + static int fpscount = 0; + static unsigned long lasttime = 0; + if (Common::Timer::GetTimeMs() - lasttime >= 1000) + { + lasttime = Common::Timer::GetTimeMs(); + s_fps = fpscount; + fpscount = 0; + } + if (XFBWrited) + ++fpscount; + + // set default viewport and scissor, for the clear to work correctly + stats.ResetFrame(); + + // done. Show our work ;) + g_renderer->Present(); + + // resize the back buffers NOW to avoid flickering when resizing windows + if (xfbchanged || window_resized || last_efbscale != g_ActiveConfig.iEFBScale) + { + g_renderer->GetBackBufferSize(&s_backbuffer_width, &s_backbuffer_height); + + FramebufferSize(s_backbuffer_width, s_backbuffer_height); + + g_renderer->RecreateFramebufferManger(); + } + + // begin next frame + g_renderer->RestoreAPIState(); + + g_renderer->BeginFrame(); + + g_renderer->UpdateViewport(); + VertexShaderManager::SetViewportChanged(); + g_VideoInitialize.pCopiedToXFB(XFBWrited || g_ActiveConfig.bUseRealXFB); + XFBWrited = false; +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/Renderer.h b/Source/Plugins/Plugin_VideoMerge/Src/Renderer.h new file mode 100644 index 0000000000..980325d79d --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/Renderer.h @@ -0,0 +1,99 @@ + +#ifndef _RENDER_H_ +#define _RENDER_H_ + +#include "VideoCommon.h" +#include "CommonTypes.h" +#include "FramebufferManager.h" + +class RendererBase +{ +public: + RendererBase(); + virtual ~RendererBase() {} + + static void FramebufferSize(int w, int h); + + static void RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + + virtual u32 AccessEFB(EFBAccessType type, int x, int y) = 0; + + static void Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,const EFBRectangle& rc); + + // virtual funcs used in Swap() + virtual void PrepareXFBCopy(const TargetRectangle &dst_rect) = 0; + virtual void Draw(const XFBSourceBase* xfbSource, const TargetRectangle& sourceRc, + const MathUtil::Rectangle& drawRc, const EFBRectangle& rc) = 0; + virtual void EndFrame() = 0; + virtual void Present() = 0; + virtual bool CheckForResize() = 0; + virtual void GetBackBufferSize(int* w, int* h) = 0; + virtual void RecreateFramebufferManger() = 0; + virtual void BeginFrame() = 0; + + // hmm + virtual bool SetScissorRect() = 0; + static bool SetScissorRect(EFBRectangle &rc); + + static TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc); + + virtual void UpdateViewport() = 0; + virtual void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) = 0; + + virtual void ResetAPIState() = 0; + virtual void RestoreAPIState() = 0; + + static int GetTargetWidth() { return s_target_width; } + static int GetTargetHeight() { return s_target_height; } + static int GetFullTargetWidth() { return s_Fulltarget_width; } + static int GetFullTargetHeight() { return s_Fulltarget_height; } + static float GetTargetScaleX() { return EFBxScale; } + static float GetTargetScaleY() { return EFByScale; } + static int GetFrameBufferWidth() { return s_backbuffer_width; } + static int GetFrameBufferHeight() { return s_backbuffer_height; } + static float GetXFBScaleX() { return xScale; } + static float GetXFBScaleY() { return yScale; } + + virtual void SetColorMask() {} + virtual void SetBlendMode(bool forceUpdate) {} + virtual void SetSamplerState(int stage, int texindex) {} + virtual void SetDitherMode() {} + virtual void SetLineWidth() {} + virtual void SetInterlacingMode() {} + virtual void SetGenerationMode() = 0; + virtual void SetDepthMode() = 0; + virtual void SetLogicOpMode() = 0; + +protected: + + // TODO: are all of these needed? + static int s_target_width; + static int s_target_height; + + static int s_Fulltarget_width; + static int s_Fulltarget_height; + + static int s_backbuffer_width; + static int s_backbuffer_height; + // + + static int s_fps; + + static u32 s_blendMode; + static bool XFBWrited; + + // these go with the settings in the GUI 1x,2x,3x,etc + static float EFBxScale; + static float EFByScale; + // these seem be the fractional value + // TODO: are these ones needed? + static float xScale; + static float yScale; + + static int s_XFB_width; + static int s_XFB_height; + + static volatile u32 s_swapRequested; +}; + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/TextureCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/TextureCache.cpp new file mode 100644 index 0000000000..df2bc6fb5a --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/TextureCache.cpp @@ -0,0 +1,525 @@ + +// Common +#include "MemoryUtil.h" + +#include "TextureCache.h" + +#include "VideoConfig.h" +#include "TextureDecoder.h" +#include "HiresTextures.h" + +#include "Statistics.h" + +#include "Main.h" + +TextureCacheBase::TexCache TextureCacheBase::textures; +u8 *TextureCacheBase::temp; + +enum +{ + TEMP_SIZE = (1024 * 1024 * 4), + TEXTURE_KILL_THRESHOLD = 200, +}; + +// returns the exponent of the smallest power of two which is greater than val +unsigned int GetPow2(unsigned int val) +{ + unsigned int ret = 0; + for (; val; val >>= 1) + ++ret; + return ret; +} + +TextureCacheBase::TCacheEntryBase::~TCacheEntryBase() +{ + // TODO: can we just use (addr) and remove the other checks? + // will need changes to TextureCache::Load and CopyRenderTargetToTexture + if (addr && false == (isRenderTarget || g_ActiveConfig.bSafeTextureCache)) + { + u32* ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr); + if (ptr && *ptr == hash) + *ptr = oldpixel; + } +} + +bool TextureCacheBase::TCacheEntryBase::IntersectsMemoryRange(u32 range_address, u32 range_size) const +{ + if (addr + size_in_bytes < range_address) + return false; + + if (addr >= range_address + range_size) + return false; + + return true; +} + +TextureCacheBase::TextureCacheBase() +{ + temp = (u8*)AllocateMemoryPages(TEMP_SIZE); + TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter); + HiresTextures::Init(g_globals->unique_id); +} + +void TextureCacheBase::Cleanup() +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + while (iter != tcend) + { + if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second->frameCount) + { + delete iter->second; + textures.erase(iter++); + } + else + ++iter; + } +} + +TextureCacheBase::~TextureCacheBase() +{ + Invalidate(true); + FreeMemoryPages(temp, TEMP_SIZE); + temp = NULL; +} + +void TextureCacheBase::ClearRenderTargets() +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + for (; iter!=tcend; ++iter) + iter->second->isRenderTarget = false; +} + +void TextureCacheBase::MakeRangeDynamic(u32 start_address, u32 size) +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + for (; iter!=tcend; ++iter) + { + // TODO: an int ?? + int rangePosition = iter->second->IntersectsMemoryRange(start_address, size); + if (0 == rangePosition) + iter->second->hash = 0; + } +} + +void TextureCacheBase::Invalidate(bool shutdown) +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + for (; iter!=tcend; ++iter) + { + // TODO: this could be better + if (shutdown) + iter->second->addr = 0; // hax, not even helpin + delete iter->second; + } + + textures.clear(); + HiresTextures::Shutdown(); +} + +void TextureCacheBase::InvalidateRange(u32 start_address, u32 size) +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + while (iter != tcend) + { + if (iter->second->IntersectsMemoryRange(start_address, size)) + { + delete iter->second; + textures.erase(iter++); + } + else + ++iter; + } +} + +TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(unsigned int stage, + u32 address, unsigned int width, unsigned int height, unsigned int tex_format, + unsigned int tlutaddr, unsigned int tlutfmt, bool UseNativeMips, unsigned int maxlevel) +{ + // necessary? + if (0 == address) + return NULL; + + u8* ptr = g_VideoInitialize.pGetMemoryPointer(address); + + // TexelSizeInNibbles(format)*width*height/16; + const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(tex_format) - 1; + const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(tex_format) - 1; + + unsigned int expandedWidth = (width + bsw) & (~bsw); + unsigned int expandedHeight = (height + bsh) & (~bsh); + + u64 hash_value = 0; + u64 texHash = 0; + u32 texID = address; + u32 FullFormat = tex_format; + u32 size_in_bytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format); + + switch (tex_format) + { + case GX_TF_C4: + case GX_TF_C8: + case GX_TF_C14X2: + FullFormat = tex_format | (tlutfmt << 16); + break; + + default: + break; + } + + // hires textures and texture dumping not supported, yet + if (g_ActiveConfig.bSafeTextureCache/* || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures*/) + { + texHash = GetHash64(ptr, size_in_bytes, g_ActiveConfig.iSafeTextureCache_ColorSamples); + + switch (tex_format) + { + case GX_TF_C4: + case GX_TF_C8: + case GX_TF_C14X2: + { + // WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up) + // tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower. + // This trick (to change the texID depending on the TLUT addr) is a trick to get around + // an issue with metroid prime's fonts, where it has multiple sets of fonts on top of + // each other stored in a single texture, and uses the palette to make different characters + // visible or invisible. Thus, unless we want to recreate the textures for every drawn character, + // we must make sure that texture with different tluts get different IDs. + const u64 tlutHash = GetHash64(texMem + tlutaddr, TexDecoder_GetPaletteSize(tex_format), + g_ActiveConfig.iSafeTextureCache_ColorSamples); + + texHash ^= tlutHash; + + if (g_ActiveConfig.bSafeTextureCache) + texID ^= ((u32)tlutHash) ^ (tlutHash >> 32); + } + break; + + default: + break; + } + + if (g_ActiveConfig.bSafeTextureCache) + hash_value = texHash; + } + + bool skip_texture_create = false; + + TCacheEntryBase *entry = textures[texID]; + if (entry) + { + if (!g_ActiveConfig.bSafeTextureCache) + hash_value = *(u32*)ptr; + + // TODO: Is the (entry->MipLevels == maxlevel) check needed? + if (entry->isRenderTarget || + (address == entry->addr && hash_value == entry->hash && + FullFormat == entry->fmt && entry->MipLevels == maxlevel)) + { + goto return_entry; + } + else + { + // Let's reload the new texture data into the same texture, + // instead of destroying it and having to create a new one. + // Might speed up movie playback very, very slightly. + + // TODO: Is the (entry->MipLevels < maxlevel) check needed? + if (width == entry->w && height==entry->h && + FullFormat == entry->fmt && entry->MipLevels < maxlevel) + { + goto load_texture; + } + else + { + delete entry; + } + } + } + + // create the texture + + const bool isPow2 = !((width & (width - 1)) || (height & (height - 1))); + unsigned int TexLevels = (isPow2 && UseNativeMips && maxlevel) ? GetPow2(std::max(width, height)) : !isPow2; + + // TODO: what is ((maxlevel + 1) && maxlevel) ? + if (TexLevels > (maxlevel + 1) && maxlevel) + TexLevels = maxlevel + 1; + + const PC_TexFormat pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, + expandedHeight, tex_format, tlutaddr, tlutfmt, true); + + textures[texID] = entry = CreateTexture(width, height, expandedWidth, TexLevels, pcfmt); + + entry->oldpixel = *(u32*)ptr; + entry->addr = address; + entry->w = width; + entry->h = height; + entry->fmt = FullFormat; + entry->MipLevels = maxlevel; + entry->size_in_bytes = size_in_bytes; + + entry->isRenderTarget = false; + entry->isNonPow2 = false; + + if (g_ActiveConfig.bSafeTextureCache) + entry->hash = hash_value; + else + // WTF is this rand() doing here? + entry->hash = *(u32*)ptr = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF); + +load_texture: + + entry->Load(width, height, expandedWidth, 0); + + if (TexLevels > 1 && pcfmt != PC_TEX_FMT_NONE) + { + const unsigned int bsdepth = TexDecoder_GetTexelSizeInNibbles(tex_format); + + unsigned int level = 1; + unsigned int mipWidth = (width + 1) >> 1; + unsigned int mipHeight = (height + 1) >> 1; + ptr += entry->size_in_bytes; + + while ((mipHeight || mipWidth) && (level < TexLevels)) + { + const unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1; + const unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1; + + expandedWidth = (currentWidth + bsw) & (~bsw); + expandedHeight = (currentHeight + bsh) & (~bsh); + + TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt, true); + //entry->Load(currentWidth, currentHeight, expandedWidth, level); + + ptr += ((max(mipWidth, bsw) * max(mipHeight, bsh) * bsdepth) >> 1); + mipWidth >>= 1; + mipHeight >>= 1; + ++level; + } + } + + INCSTAT(stats.numTexturesCreated); + SETSTAT(stats.numTexturesAlive, (int)textures.size()); + +return_entry: + + entry->frameCount = frameCount; + entry->Bind(stage); + + return entry; +} + +void TextureCacheBase::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, + bool bIsIntensityFmt, u32 copyfmt, bool bScaleByHalf, const EFBRectangle &source_rect) +{ + float colmat[20] = {}; + // last four floats for fConstAdd + float *const fConstAdd = colmat + 16; + unsigned int cbufid = -1; + + // TODO: Move this to TextureCache::Init() + if (bFromZBuffer) + { + switch (copyfmt) + { + case 0: // Z4 + case 1: // Z8 + colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1.0f; + cbufid = 12; + break; + + case 3: // Z16 //? + colmat[1] = colmat[5] = colmat[9] = colmat[12] = 1.0f; + cbufid = 13; + break; + + case 11: // Z16 (reverse order) + colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1.0f; + cbufid = 14; + break; + + case 6: // Z24X8 + colmat[0] = colmat[5] = colmat[10] = 1.0f; + cbufid = 15; + break; + + case 9: // Z8M + colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1.0f; + cbufid = 16; + break; + + case 10: // Z8L + colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1.0f; + cbufid = 17; + break; + + case 12: // Z16L + colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1.0f; + cbufid = 18; + break; + + default: + ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt); + colmat[2] = colmat[5] = colmat[8] = 1.0f; + cbufid = 19; + break; + } + } + else if (bIsIntensityFmt) + { + fConstAdd[0] = fConstAdd[1] = fConstAdd[2] = 16.0f/255.0f; + switch (copyfmt) + { + case 0: // I4 + case 1: // I8 + case 2: // IA4 + case 3: // IA8 + // TODO - verify these coefficients + colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f; + colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f; + colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f; + + if (copyfmt < 2) + { + fConstAdd[3] = 16.0f / 255.0f; + colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f; + cbufid = 0; + } + else// alpha + { + colmat[15] = 1; + cbufid = 1; + } + + break; + + default: + ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%x", copyfmt); + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + break; + } + } + else + { + switch (copyfmt) + { + case 0: // R4 + case 8: // R8 + colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1; + cbufid = 2; + break; + + case 2: // RA4 + case 3: // RA8 + colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1; + cbufid = 3; + break; + + case 7: // A8 + colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1; + cbufid = 4; + break; + + case 9: // G8 + colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1; + cbufid = 5; + break; + + case 10: // B8 + colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1; + cbufid = 6; + break; + + case 11: // RG8 + colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1; + cbufid = 7; + break; + + case 12: // GB8 + colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1; + cbufid = 8; + break; + + case 4: // RGB565 + colmat[0] = colmat[5] = colmat[10] = 1; + fConstAdd[3] = 1; // set alpha to 1 + cbufid = 9; + break; + + case 5: // RGB5A3 + case 6: // RGBA8 + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + cbufid = 10; + break; + + default: + ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt); + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + cbufid = 11; + break; + } + } + + const int tex_w = (abs(source_rect.GetWidth()) >> bScaleByHalf); + const int tex_h = (abs(source_rect.GetHeight()) >> bScaleByHalf); + + const int scaled_tex_w = g_ActiveConfig.bCopyEFBScaled ? (int)(tex_w * g_renderer->GetTargetScaleX()) : tex_w; + const int scaled_tex_h = g_ActiveConfig.bCopyEFBScaled ? (int)(tex_h * g_renderer->GetTargetScaleY()) : tex_h; + + TCacheEntryBase* entry = NULL; + + const TexCache::iterator iter = textures.find(address); + if (textures.end() != iter) + { + entry = iter->second; + + if (entry->isRenderTarget && entry->Scaledw == scaled_tex_w && entry->Scaledh == scaled_tex_h) + { + goto load_texture; + } + else + { + // remove it and recreate it as a render target + delete entry; + textures.erase(iter); + } + } + + // create the texture + textures[address] = entry = CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h); + + if (NULL == entry) + PanicAlert("CopyRenderTargetToTexture failed to create entry.texture at %s %d\n", __FILE__, __LINE__); + + entry->addr = 0; // TODO: probably can use this and eliminate isRenderTarget + entry->hash = 0; + entry->w = tex_w; + entry->h = tex_h; + entry->Scaledw = scaled_tex_w; + entry->Scaledh = scaled_tex_h; + entry->fmt = copyfmt; + + entry->isRenderTarget = true; + entry->isNonPow2 = true; // TODO: is this used anywhere? + +load_texture: + + entry->frameCount = frameCount; + + g_renderer->ResetAPIState(); // reset any game specific settings + + // load the texture + entry->FromRenderTarget(bFromZBuffer, bScaleByHalf, cbufid, colmat, source_rect); + + g_renderer->RestoreAPIState(); +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/TextureCache.h b/Source/Plugins/Plugin_VideoMerge/Src/TextureCache.h new file mode 100644 index 0000000000..49c25dbc4a --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/TextureCache.h @@ -0,0 +1,80 @@ + +#ifndef _TEXTURECACHE_H_ +#define _TEXTURECACHE_H_ + +#include + +#include "CommonTypes.h" +#include "VideoCommon.h" +#include "TextureDecoder.h" + +unsigned int GetPow2(unsigned int val); + +class TextureCacheBase +{ +public: + struct TCacheEntryBase + { + u32 addr; + u32 size_in_bytes; + u64 hash; + u32 paletteHash; + u32 oldpixel; + + int frameCount; + unsigned int w, h, fmt, MipLevels; + int Scaledw, Scaledh; + + bool isRenderTarget; + bool isNonPow2; + + TCacheEntryBase() : addr(0), size_in_bytes(0), hash(0), paletteHash(0), oldpixel(0), + frameCount(0), w(0), h(0), fmt(0), MipLevels(0), Scaledw(0), Scaledh(0), + isRenderTarget(false), isNonPow2(true) {} + + virtual ~TCacheEntryBase(); + + virtual void Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int levels) = 0; + + virtual void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect) = 0; + + virtual void Bind(unsigned int stage) = 0; + + bool IntersectsMemoryRange(u32 range_address, u32 range_size) const; + }; + + friend struct TCacheEntryBase; + + TextureCacheBase(); + virtual ~TextureCacheBase(); + + static void Cleanup(); + + static void Invalidate(bool shutdown); + static void InvalidateRange(u32 start_address, u32 size); + + // TODO: make these 2 static? + TCacheEntryBase* Load(unsigned int stage, u32 address, unsigned int width, + unsigned int height, unsigned int tex_format, unsigned int tlutaddr, + unsigned int tlutfmt, bool UseNativeMips, unsigned int maxlevel); + + void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, + u32 copyfmt, bool bScaleByHalf, const EFBRectangle &source_rect); + + virtual TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) = 0; + + virtual TCacheEntryBase* CreateRenderTargetTexture(int scaled_tex_w, int scaled_tex_h) = 0; + + static void ClearRenderTargets(); + static void MakeRangeDynamic(u32 start_address, u32 size); + +protected: + typedef std::map TexCache; + static TexCache textures; + static u8 *temp; +}; + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoMerge/Src/VertexManager.cpp new file mode 100644 index 0000000000..8a01541130 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/VertexManager.cpp @@ -0,0 +1,268 @@ + +// VideoCommon + +#include "CommonTypes.h" + +#include "OpcodeDecoding.h" +#include "IndexGenerator.h" +#include "Statistics.h" +#include "VideoConfig.h" +#include "PixelShaderManager.h" +#include "NativeVertexFormat.h" +#include "VertexShaderManager.h" +#include "Profiler.h" + +// +#include "TextureCache.h" +#include "VertexManager.h" +#include "Main.h" + +u16* VertexManagerBase::TIBuffer; +u16* VertexManagerBase::LIBuffer; +u16* VertexManagerBase::PIBuffer; +bool VertexManagerBase::Flushed; + +int VertexManagerBase::lastPrimitive; +u8* VertexManagerBase::LocalVBuffer; + +// TODO: put this in .h perhaps +extern NativeVertexFormat* g_nativeVertexFmt; + +NativeVertexFormat* NativeVertexFormat::Create() +{ + return g_vertex_manager->CreateNativeVertexFormat(); +} + +void VertexManagerBase::ResetBuffer() +{ + s_pCurBufferPointer = LocalVBuffer; +} + +VertexManagerBase::VertexManagerBase() +{ + LocalVBuffer = new u8[MAXVBUFFERSIZE]; + TIBuffer = new u16[MAXIBUFFERSIZE]; + LIBuffer = new u16[MAXIBUFFERSIZE]; + PIBuffer = new u16[MAXIBUFFERSIZE]; + + s_pCurBufferPointer = LocalVBuffer; + s_pBaseBufferPointer = LocalVBuffer; + + Flushed = false; + + IndexGenerator::Start(TIBuffer, LIBuffer, PIBuffer); +} + +VertexManagerBase::~VertexManagerBase() +{ + delete[] LocalVBuffer; + delete[] TIBuffer; + delete[] LIBuffer; + delete[] PIBuffer; + + // most likely not needed + ResetBuffer(); +} + +void VertexManagerBase::AddIndices(int _primitive, int _numVertices) +{ + switch (_primitive) + { + case GX_DRAW_QUADS: + IndexGenerator::AddQuads(_numVertices); + break; + + case GX_DRAW_TRIANGLES: + IndexGenerator::AddList(_numVertices); + break; + + case GX_DRAW_TRIANGLE_STRIP: + IndexGenerator::AddStrip(_numVertices); + break; + + case GX_DRAW_TRIANGLE_FAN: + IndexGenerator::AddFan(_numVertices); + break; + + case GX_DRAW_LINE_STRIP: + IndexGenerator::AddLineStrip(_numVertices); + break; + + case GX_DRAW_LINES: + IndexGenerator::AddLineList(_numVertices); + break; + + case GX_DRAW_POINTS: + IndexGenerator::AddPoints(_numVertices); + break; + } +} + +int VertexManager::GetRemainingSize() +{ + return VertexManagerBase::GetRemainingSize(); +} + +int VertexManagerBase::GetRemainingSize() +{ + return MAXVBUFFERSIZE - (int)(s_pCurBufferPointer - LocalVBuffer); + return 0; +} + +int VertexManagerBase::GetRemainingVertices(int primitive) +{ + switch (primitive) + { + case GX_DRAW_QUADS: + case GX_DRAW_TRIANGLES: + case GX_DRAW_TRIANGLE_STRIP: + case GX_DRAW_TRIANGLE_FAN: + return (MAXIBUFFERSIZE - IndexGenerator::GetTriangleindexLen()) / 3; + break; + + case GX_DRAW_LINE_STRIP: + case GX_DRAW_LINES: + return (MAXIBUFFERSIZE - IndexGenerator::GetLineindexLen()) / 2; + break; + + case GX_DRAW_POINTS: + return (MAXIBUFFERSIZE - IndexGenerator::GetPointindexLen()); + break; + + default: + return 0; + break; + } +} + +void VertexManager::AddVertices(int _primitive, int _numVertices) +{ + VertexManagerBase::AddVertices(_primitive, _numVertices); +} + +void VertexManagerBase::AddVertices(int _primitive, int _numVertices) +{ + if (_numVertices <= 0) + return; + + switch (_primitive) + { + case GX_DRAW_QUADS: + case GX_DRAW_TRIANGLES: + case GX_DRAW_TRIANGLE_STRIP: + case GX_DRAW_TRIANGLE_FAN: + if (MAXIBUFFERSIZE - IndexGenerator::GetTriangleindexLen() < 3 * _numVertices) + g_vertex_manager->Flush(); + break; + + case GX_DRAW_LINE_STRIP: + case GX_DRAW_LINES: + if (MAXIBUFFERSIZE - IndexGenerator::GetLineindexLen() < 2 * _numVertices) + g_vertex_manager->Flush(); + break; + + case GX_DRAW_POINTS: + if (MAXIBUFFERSIZE - IndexGenerator::GetPointindexLen() < _numVertices) + g_vertex_manager->Flush(); + break; + + default: + return; + break; + } + + if (Flushed) + { + IndexGenerator::Start(TIBuffer, LIBuffer, PIBuffer); + Flushed = false; + } + + lastPrimitive = _primitive; + ADDSTAT(stats.thisFrame.numPrims, _numVertices); + INCSTAT(stats.thisFrame.numPrimitiveJoins); + AddIndices(_primitive, _numVertices); +} + +void VertexManager::Flush() +{ + VertexManagerBase::Flush(); +} + +void VertexManagerBase::Flush() +{ + if (LocalVBuffer == s_pCurBufferPointer) + return; + + if (Flushed) + return; + + Flushed = true; + + VideoFifo_CheckEFBAccess(); + + DVSTARTPROFILE(); + + u32 usedtextures = 0; + for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i) + { + if (bpmem.tevorders[i/2].getEnable(i & 1)) + usedtextures |= 1 << bpmem.tevorders[i/2].getTexMap(i & 1); + } + + if (bpmem.genMode.numindstages > 0) + for (unsigned int i = 0; i < bpmem.genMode.numtevstages + 1; ++i) + if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) + usedtextures |= 1 << bpmem.tevindref.getTexMap(bpmem.tevind[i].bt); + + for (unsigned int i = 0; i < 8; ++i) + { + if (usedtextures & (1 << i)) + { + g_renderer->SetSamplerState(i & 3, i >> 2); + + FourTexUnits &tex = bpmem.tex[i >> 2]; + + TextureCacheBase::TCacheEntryBase* tentry = g_texture_cache->Load(i, + (tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5, + tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1, + tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9, + tex.texTlut[i&3].tlut_format, + (tex.texMode0[i&3].min_filter & 3) && (tex.texMode0[i&3].min_filter != 8) && g_ActiveConfig.bUseNativeMips, + (tex.texMode1[i&3].max_lod >> 4)); + + if (tentry) + PixelShaderManager::SetTexDims(i, tentry->w, tentry->h, 0, 0); + else + ERROR_LOG(VIDEO, "error loading texture"); + } + } + + VertexShaderManager::SetConstants(); + PixelShaderManager::SetConstants(); + + if (false == g_pixel_shader_cache->SetShader(false)) + goto shader_fail; + + if (false == g_vertex_shader_cache->SetShader(g_nativeVertexFmt->m_components)) + goto shader_fail; + + const unsigned int stride = g_nativeVertexFmt->GetVertexStride(); + g_nativeVertexFmt->SetupVertexPointers(); + + // TODO: + g_vertex_manager->Draw(stride, false); + + if (false == g_ActiveConfig.bDstAlphaPass && bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate) + { + if (false == g_pixel_shader_cache->SetShader(true)) + goto shader_fail; + + // update alpha only + g_vertex_manager->Draw(stride, true); + } + + //IndexGenerator::Start(TIBuffer, LIBuffer, PIBuffer); + +shader_fail: + ResetBuffer(); +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/VertexManager.h b/Source/Plugins/Plugin_VideoMerge/Src/VertexManager.h new file mode 100644 index 0000000000..a445d5f8ab --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/VertexManager.h @@ -0,0 +1,47 @@ + +#ifndef _VERTExMANAGER_H_ +#define _VERTExMANAGER_H_ + +#include "NativeVertexFormat.h" +#include "NativeVertexWriter.h" +#include "BPMemory.h" + +enum +{ + MAXVBUFFERSIZE = 0x50000, + MAXIBUFFERSIZE = 0x10000, +}; + +using VertexManager::s_pBaseBufferPointer; +using VertexManager::s_pCurBufferPointer; + +extern NativeVertexFormat *g_nativeVertexFmt; + +class VertexManagerBase +{ +public: + VertexManagerBase(); + virtual ~VertexManagerBase(); + + static void Flush(); + virtual void Draw(u32 stride, bool alphapass) = 0; + + virtual NativeVertexFormat* CreateNativeVertexFormat() = 0; + + static void AddIndices(int _primitive, int _numVertices); + static int GetRemainingSize(); + static int GetRemainingVertices(int primitive); + static void AddVertices(int _primitive, int _numVertices); + static void ResetBuffer(); + +protected: + static u16* TIBuffer; + static u16* LIBuffer; + static u16* PIBuffer; + static bool Flushed; + + static int lastPrimitive; + static u8* LocalVBuffer; +}; + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/VertexShaderCache.cpp b/Source/Plugins/Plugin_VideoMerge/Src/VertexShaderCache.cpp new file mode 100644 index 0000000000..6ac455e514 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/VertexShaderCache.cpp @@ -0,0 +1,25 @@ + +#include "VertexShaderManager.h" + +#include "Main.h" +#include "VertexShaderCache.h" + +void SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + g_vertex_shader_cache->SetVSConstant4f(const_number, f1, f2, f3, f4); +} + +void SetVSConstant4fv(unsigned int const_number, const float* f) +{ + g_vertex_shader_cache->SetVSConstant4fv(const_number, f); +} + +void SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float* f) +{ + g_vertex_shader_cache->SetMultiVSConstant3fv(const_number, count, f); +} + +void SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float* f) +{ + g_vertex_shader_cache->SetMultiVSConstant4fv(const_number, count, f); +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/VertexShaderCache.h b/Source/Plugins/Plugin_VideoMerge/Src/VertexShaderCache.h new file mode 100644 index 0000000000..c8b47cd644 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/VertexShaderCache.h @@ -0,0 +1,18 @@ + +#ifndef _VERTEXSHADERCACHE_H_ +#define _VERTEXSHADERCACHE_H_ + +class VertexShaderCacheBase +{ +public: + virtual ~VertexShaderCacheBase() {} + + virtual bool SetShader(u32 components) = 0; + + virtual void SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) = 0; + virtual void SetVSConstant4fv(unsigned int const_number, const float* f) = 0; + virtual void SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float* f) = 0; + virtual void SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float* f) = 0; +}; + +#endif diff --git a/Source/Plugins/Plugin_VideoMerge/Src/VideoConfigDiag.cpp b/Source/Plugins/Plugin_VideoMerge/Src/VideoConfigDiag.cpp new file mode 100644 index 0000000000..5ef26c896f --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/VideoConfigDiag.cpp @@ -0,0 +1,245 @@ + +#include "VideoConfigDiag.h" + +#include "VideoConfig.h" +#include "Main.h" + +#define _connect_macro_(b, f, c, s) (b)->Connect(wxID_ANY, (c), wxCommandEventHandler( f ), (wxObject*)0, (wxEvtHandler*)s) + +// template instantiation +template class BoolSetting; +template class BoolSetting; + +typedef BoolSetting SettingCheckBox; +typedef BoolSetting SettingRadioButton; + +SettingCheckBox::BoolSetting(wxWindow* parent, const wxString& label, bool &setting, bool reverse, long style) + : wxCheckBox(parent, -1, label, wxDefaultPosition, wxDefaultSize, style) + , m_setting(setting) + , m_reverse(reverse) +{ + SetValue(m_setting ^ m_reverse); + _connect_macro_(this, BoolSetting::UpdateValue, wxEVT_COMMAND_CHECKBOX_CLICKED, this); +} + +SettingRadioButton::BoolSetting(wxWindow* parent, const wxString& label, bool &setting, bool reverse, long style) + : wxRadioButton(parent, -1, label, wxDefaultPosition, wxDefaultSize, style) + , m_setting(setting) + , m_reverse(reverse) +{ + SetValue(m_setting ^ m_reverse); + _connect_macro_(this, BoolSetting::UpdateValue, wxEVT_COMMAND_RADIOBUTTON_SELECTED, this); +} + +template +void BoolSetting::UpdateValue(wxCommandEvent& ev) +{ + m_setting = (ev.GetInt() != 0) ^ m_reverse; +} + +SettingChoice::SettingChoice(wxWindow* parent, int &setting, int num, const wxString choices[]) + : wxChoice(parent, -1, wxDefaultPosition, wxDefaultSize, num, choices) + , m_setting(setting) +{ + Select(m_setting); + _connect_macro_(this, SettingChoice::UpdateValue, wxEVT_COMMAND_CHOICE_SELECTED, this); +} + +void SettingChoice::UpdateValue(wxCommandEvent& ev) +{ + m_setting = ev.GetInt(); +} + +void VideoConfigDiag::CloseDiag(wxCommandEvent&) +{ + Close(); +} + +VideoConfigDiag::VideoConfigDiag(wxWindow* parent) + : wxDialog(parent, -1, wxT("Dolphin Graphics Configuration"), wxDefaultPosition, wxDefaultSize) +{ + VideoConfig &vconfig = g_Config; + + wxNotebook* const notebook = new wxNotebook(this, -1, wxDefaultPosition, wxDefaultSize); + + // -- GENERAL -- + { + wxPanel* const page_general = new wxPanel(notebook, -1, wxDefaultPosition); + notebook->AddPage(page_general, wxT("General")); + wxBoxSizer* const szr_general = new wxBoxSizer(wxVERTICAL); + + // - basic + { + wxStaticBoxSizer* const group_basic = new wxStaticBoxSizer(wxVERTICAL, page_general, wxT("Basic")); + szr_general->Add(group_basic, 0, wxEXPAND | wxALL, 5); + wxFlexGridSizer* const szr_basic = new wxFlexGridSizer(2, 5, 5); + group_basic->Add(szr_basic, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + + // graphics api + { + const wxString gfxapi_choices[] = { wxT("Software [not present]"), + wxT("OpenGL [broken]"), wxT("Direct3D 9 [broken]"), wxT("Direct3D 11") }; + + szr_basic->Add(new wxStaticText(page_general, -1, wxT("Graphics API:")), 1, wxALIGN_CENTER_VERTICAL, 0); + wxChoice* const choice_gfxapi = new SettingChoice(page_general, + g_gfxapi, sizeof(gfxapi_choices)/sizeof(*gfxapi_choices), gfxapi_choices); + szr_basic->Add(choice_gfxapi, 1, 0, 0); + } + + // for D3D only + // adapter + { + //szr_basic->Add(new wxStaticText(page_general, -1, wxT("Adapter:")), 1, wxALIGN_CENTER_VERTICAL, 5); + //wxChoice* const choice_adapter = new SettingChoice(page_general, vconfig.iAdapter); + //szr_basic->Add(choice_adapter, 1, 0, 0); + } + + // aspect-ratio + { + const wxString ar_choices[] = { wxT("Auto [recommended]"), + wxT("Force 16:9"), wxT("Force 4:3"), wxT("Strech to Window") }; + + szr_basic->Add(new wxStaticText(page_general, -1, wxT("Aspect ratio:")), 1, wxALIGN_CENTER_VERTICAL, 0); + wxChoice* const choice_aspect = new SettingChoice(page_general, + vconfig.iAspectRatio, sizeof(ar_choices)/sizeof(*ar_choices), ar_choices); + szr_basic->Add(choice_aspect, 1, 0, 0); + } + + // widescreen hack + { + szr_basic->AddStretchSpacer(1); + szr_basic->Add(new SettingCheckBox(page_general, wxT("Widescreen Hack"), vconfig.bWidescreenHack), 1, 0, 0); + szr_basic->AddStretchSpacer(1); + szr_basic->Add(new SettingCheckBox(page_general, wxT("V-Sync"), vconfig.bVSync), 1, 0, 0); + } + + // - EFB + { + wxStaticBoxSizer* const group_efb = new wxStaticBoxSizer(wxVERTICAL, page_general, wxT("EFB")); + szr_general->Add(group_efb, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + + group_efb->Add(new SettingCheckBox(page_general, wxT("Enable CPU Access"), vconfig.bEFBAccessEnable), 0, wxBOTTOM | wxLEFT, 5); + + // EFB scale + { + // TODO: give this a label + const wxString efbscale_choices[] = { wxT("Fractional"), wxT("Integral [recommended]"), + wxT("1x"), wxT("2x"), wxT("3x")/*, wxT("4x")*/ }; + + wxChoice *const choice_efbscale = new SettingChoice(page_general, + vconfig.iEFBScale, sizeof(efbscale_choices)/sizeof(*efbscale_choices), efbscale_choices); + group_efb->Add(choice_efbscale, 0, wxBOTTOM | wxLEFT, 5); + } + + // EFB copy + wxStaticBoxSizer* const group_efbcopy = new wxStaticBoxSizer(wxHORIZONTAL, page_general, wxT("Copy")); + group_efb->Add(group_efbcopy, 0, wxEXPAND | wxBOTTOM, 5); + + group_efbcopy->Add(new SettingCheckBox(page_general, wxT("Enable"), vconfig.bEFBCopyDisable, true), 0, wxLEFT | wxRIGHT | wxBOTTOM, 5); + group_efbcopy->AddStretchSpacer(1); + group_efbcopy->Add(new SettingRadioButton(page_general, wxT("Texture"), vconfig.bCopyEFBToTexture, false, wxRB_GROUP), 0, wxRIGHT, 5); + group_efbcopy->Add(new SettingRadioButton(page_general, wxT("RAM"), vconfig.bCopyEFBToTexture, true), 0, wxRIGHT, 5); + } + + // - safe texture cache + { + wxStaticBoxSizer* const group_safetex = new wxStaticBoxSizer(wxHORIZONTAL, page_general, wxT("Safe Texture Cache")); + szr_general->Add(group_safetex, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + + // safe texture cache + group_safetex->Add(new SettingCheckBox(page_general, wxT("Enable"), vconfig.bSafeTextureCache), 0, wxLEFT | wxRIGHT | wxBOTTOM, 5); + group_safetex->AddStretchSpacer(1); + // TODO: make these radio buttons functional + //group_safetex->Add(new wxRadioButton(page_general, -1, wxT("Safe"), + // wxDefaultPosition, wxDefaultSize, wxRB_GROUP), 0, wxRIGHT, 5); + //group_safetex->Add(new wxRadioButton(page_general, -1, wxT("Normal")), 0, wxRIGHT, 5); + //group_safetex->Add(new wxRadioButton(page_general, -1, wxT("Fast")), 0, wxRIGHT, 5); + } + + } + + page_general->SetSizerAndFit(szr_general); + } + + // -- ADVANCED -- + { + wxPanel* const page_advanced = new wxPanel(notebook, -1, wxDefaultPosition); + notebook->AddPage(page_advanced, wxT("Advanced")); + wxBoxSizer* const szr_advanced = new wxBoxSizer(wxVERTICAL); + + // - rendering + { + wxStaticBoxSizer* const group_rendering = new wxStaticBoxSizer(wxVERTICAL, page_advanced, wxT("Rendering")); + szr_advanced->Add(group_rendering, 0, wxEXPAND | wxALL, 5); + wxGridSizer* const szr_rendering = new wxGridSizer(2, 5, 5); + group_rendering->Add(szr_rendering, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + + szr_rendering->Add(new SettingCheckBox(page_advanced, wxT("Enable Wireframe"), vconfig.bWireFrame)); + szr_rendering->Add(new SettingCheckBox(page_advanced, wxT("Disable Lighting"), vconfig.bDisableLighting)); + szr_rendering->Add(new SettingCheckBox(page_advanced, wxT("Disable Textures"), vconfig.bDisableTexturing)); + szr_rendering->Add(new SettingCheckBox(page_advanced, wxT("Disable Fog"), vconfig.bDisableFog)); + szr_rendering->Add(new SettingCheckBox(page_advanced, wxT("Disable Dest. Alpha Pass"), vconfig.bDstAlphaPass)); + } + + // - info + { + wxStaticBoxSizer* const group_info = new wxStaticBoxSizer(wxVERTICAL, page_advanced, wxT("Overlay Information")); + szr_advanced->Add(group_info, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + wxGridSizer* const szr_info = new wxGridSizer(2, 5, 5); + group_info->Add(szr_info, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + + szr_info->Add(new SettingCheckBox(page_advanced, wxT("Show FPS"), vconfig.bShowFPS)); + szr_info->Add(new SettingCheckBox(page_advanced, wxT("Various Statistics"), vconfig.bOverlayStats)); + szr_info->Add(new SettingCheckBox(page_advanced, wxT("Projection Stats"), vconfig.bOverlayProjStats)); + szr_info->Add(new SettingCheckBox(page_advanced, wxT("Texture Format"), vconfig.bTexFmtOverlayEnable)); + szr_info->Add(new SettingCheckBox(page_advanced, wxT("EFB Copy Regions"), vconfig.bShowEFBCopyRegions)); + } + + // - XFB + { + wxStaticBoxSizer* const group_xfb = new wxStaticBoxSizer(wxHORIZONTAL, page_advanced, wxT("XFB")); + szr_advanced->Add(group_xfb, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + + group_xfb->Add(new SettingCheckBox(page_advanced, wxT("Enable"), vconfig.bUseXFB), 0, wxLEFT | wxRIGHT | wxBOTTOM, 5); + group_xfb->AddStretchSpacer(1); + group_xfb->Add(new SettingRadioButton(page_advanced, wxT("Virtual"), vconfig.bUseRealXFB, true, wxRB_GROUP), 0, wxRIGHT, 5); + group_xfb->Add(new SettingRadioButton(page_advanced, wxT("Real"), vconfig.bUseRealXFB), 0, wxRIGHT, 5); + } + + // - utility + { + wxStaticBoxSizer* const group_utility = new wxStaticBoxSizer(wxVERTICAL, page_advanced, wxT("Utility")); + szr_advanced->Add(group_utility, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + wxGridSizer* const szr_utility = new wxGridSizer(2, 5, 5); + group_utility->Add(szr_utility, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + + szr_utility->Add(new SettingCheckBox(page_advanced, wxT("Dump Textures"), vconfig.bDumpTextures)); + szr_utility->Add(new SettingCheckBox(page_advanced, wxT("Load Hi-Res Textures"), vconfig.bHiresTextures)); + szr_utility->Add(new SettingCheckBox(page_advanced, wxT("Dump EFB Target"), vconfig.bDumpEFBTarget)); + szr_utility->Add(new SettingCheckBox(page_advanced, wxT("Dump Frames"), vconfig.bDumpFrames)); + szr_utility->Add(new SettingCheckBox(page_advanced, wxT("Free Look"), vconfig.bFreeLook)); + } + + // stuff to move/remove + { + szr_advanced->Add(new SettingCheckBox(page_advanced, wxT("Load Native Mipmaps"), vconfig.bUseNativeMips), 0, wxBOTTOM | wxLEFT, 5); + szr_advanced->Add(new SettingCheckBox(page_advanced, wxT("EFB Scaled Copy"), vconfig.bCopyEFBScaled), 0, wxBOTTOM | wxLEFT, 5); + szr_advanced->Add(new SettingCheckBox(page_advanced, wxT("Auto Scale"), vconfig.bAutoScale), 0, wxBOTTOM | wxLEFT, 5); + szr_advanced->Add(new SettingCheckBox(page_advanced, wxT("Crop"), vconfig.bCrop), 0, wxBOTTOM | wxLEFT, 5); + szr_advanced->Add(new SettingCheckBox(page_advanced, wxT("Enable OpenCL"), vconfig.bEnableOpenCL), 0, wxBOTTOM | wxLEFT, 5); + szr_advanced->Add(new SettingCheckBox(page_advanced, wxT("Enable Display List Caching"), vconfig.bDlistCachingEnable), 0, wxBOTTOM | wxLEFT, 5); + } + + page_advanced->SetSizerAndFit(szr_advanced); + } + + wxButton* const btn_close = new wxButton(this, -1, wxT("Close"), wxDefaultPosition); + _connect_macro_(btn_close, VideoConfigDiag::CloseDiag, wxEVT_COMMAND_BUTTON_CLICKED, this); + + wxBoxSizer* const szr_main = new wxBoxSizer(wxVERTICAL); + szr_main->Add(notebook, 1, wxEXPAND | wxALL, 5); + szr_main->Add(btn_close, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 5); + + SetSizerAndFit(szr_main); + Center(); +} diff --git a/Source/Plugins/Plugin_VideoMerge/Src/VideoConfigDiag.h b/Source/Plugins/Plugin_VideoMerge/Src/VideoConfigDiag.h new file mode 100644 index 0000000000..327f71b663 --- /dev/null +++ b/Source/Plugins/Plugin_VideoMerge/Src/VideoConfigDiag.h @@ -0,0 +1,44 @@ + +#ifndef _CONFIG_DIAG_H_ +#define _CONFIG_DIAG_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +class BoolSetting : public W +{ +public: + BoolSetting(wxWindow* parent, const wxString& label, bool &setting, bool reverse = false, long style = 0); + void UpdateValue(wxCommandEvent& ev); +private: + bool &m_setting; + const bool m_reverse; +}; + +class SettingChoice : public wxChoice +{ +public: + SettingChoice(wxWindow* parent, int &setting, int num = 0, const wxString choices[] = NULL); + void UpdateValue(wxCommandEvent& ev); +private: + int &m_setting; +}; + +class VideoConfigDiag : public wxDialog +{ +public: + VideoConfigDiag(wxWindow* parent); + +protected: + void CloseDiag(wxCommandEvent&); +}; + +#endif