diff options
Diffstat (limited to 'src/VBox/Devices/Graphics/DevVGA-SVGA3d-win-d3d9.cpp')
-rw-r--r-- | src/VBox/Devices/Graphics/DevVGA-SVGA3d-win-d3d9.cpp | 1247 |
1 files changed, 1247 insertions, 0 deletions
diff --git a/src/VBox/Devices/Graphics/DevVGA-SVGA3d-win-d3d9.cpp b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-win-d3d9.cpp new file mode 100644 index 00000000..47d52ef6 --- /dev/null +++ b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-win-d3d9.cpp @@ -0,0 +1,1247 @@ +/* $Id: DevVGA-SVGA3d-win-d3d9.cpp $ */ +/** @file + * DevVMWare - VMWare SVGA device Direct3D 9 backend. + */ + +/* + * Copyright (C) 2019-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * 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, in version 3 of the + * License. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define LOG_GROUP LOG_GROUP_DEV_VMSVGA +#include <VBox/vmm/pdmdev.h> + +#include <iprt/assert.h> + +#include "DevVGA-SVGA.h" +#include "DevVGA-SVGA3d-internal.h" + + +typedef enum D3D9TextureType +{ + D3D9TextureType_Texture, + D3D9TextureType_Bounce, + D3D9TextureType_Emulated +} D3D9TextureType; + +DECLINLINE(D3DCUBEMAP_FACES) vmsvga3dCubemapFaceFromIndex(uint32_t iFace) +{ + D3DCUBEMAP_FACES Face; + switch (iFace) + { + case 0: Face = D3DCUBEMAP_FACE_POSITIVE_X; break; + case 1: Face = D3DCUBEMAP_FACE_NEGATIVE_X; break; + case 2: Face = D3DCUBEMAP_FACE_POSITIVE_Y; break; + case 3: Face = D3DCUBEMAP_FACE_NEGATIVE_Y; break; + case 4: Face = D3DCUBEMAP_FACE_POSITIVE_Z; break; + default: + case 5: Face = D3DCUBEMAP_FACE_NEGATIVE_Z; break; + } + return Face; +} + +IDirect3DTexture9 *D3D9GetTexture(PVMSVGA3DSURFACE pSurface, + D3D9TextureType enmType) +{ + IDirect3DTexture9 *p; + switch (enmType) + { + default: AssertFailed(); + RT_FALL_THRU(); + case D3D9TextureType_Texture: p = pSurface->u.pTexture; break; + case D3D9TextureType_Bounce: p = pSurface->bounce.pTexture; break; + case D3D9TextureType_Emulated: p = pSurface->emulated.pTexture; break; + } + return p; +} + +IDirect3DCubeTexture9 *D3D9GetCubeTexture(PVMSVGA3DSURFACE pSurface, + D3D9TextureType enmType) +{ + IDirect3DCubeTexture9 *p; + switch (enmType) + { + default: AssertFailed(); + RT_FALL_THRU(); + case D3D9TextureType_Texture: p = pSurface->u.pCubeTexture; break; + case D3D9TextureType_Bounce: p = pSurface->bounce.pCubeTexture; break; + case D3D9TextureType_Emulated: p = pSurface->emulated.pCubeTexture; break; + } + return p; +} + +IDirect3DVolumeTexture9 *D3D9GetVolumeTexture(PVMSVGA3DSURFACE pSurface, + D3D9TextureType enmType) +{ + IDirect3DVolumeTexture9 *p; + switch (enmType) + { + default: AssertFailed(); + RT_FALL_THRU(); + case D3D9TextureType_Texture: p = pSurface->u.pVolumeTexture; break; + case D3D9TextureType_Bounce: p = pSurface->bounce.pVolumeTexture; break; + case D3D9TextureType_Emulated: p = pSurface->emulated.pVolumeTexture; break; + } + return p; +} + +HRESULT D3D9GetTextureLevel(PVMSVGA3DSURFACE pSurface, + D3D9TextureType enmType, + uint32_t uFace, + uint32_t uMipmap, + IDirect3DSurface9 **ppD3DSurface) +{ + HRESULT hr; + + if (pSurface->enmD3DResType == VMSVGA3D_D3DRESTYPE_CUBE_TEXTURE) + { + Assert(pSurface->cFaces == 6); + + IDirect3DCubeTexture9 *p = D3D9GetCubeTexture(pSurface, enmType); + if (p) + { + D3DCUBEMAP_FACES const FaceType = vmsvga3dCubemapFaceFromIndex(uFace); + hr = p->GetCubeMapSurface(FaceType, uMipmap, ppD3DSurface); + AssertMsg(hr == D3D_OK, ("GetCubeMapSurface failed with %x\n", hr)); + } + else + { + AssertFailed(); + hr = E_INVALIDARG; + } + } + else if (pSurface->enmD3DResType == VMSVGA3D_D3DRESTYPE_TEXTURE) + { + Assert(pSurface->cFaces == 1); + Assert(uFace == 0); + + IDirect3DTexture9 *p = D3D9GetTexture(pSurface, enmType); + if (p) + { + hr = p->GetSurfaceLevel(uMipmap, ppD3DSurface); + AssertMsg(hr == D3D_OK, ("GetSurfaceLevel failed with %x\n", hr)); + } + else + { + AssertFailed(); + hr = E_INVALIDARG; + } + } + else + { + AssertMsgFailed(("No surface level for type %d\n", pSurface->enmD3DResType)); + hr = E_INVALIDARG; + } + + return hr; +} + + +static HRESULT d3dCopyToVertexBuffer(IDirect3DVertexBuffer9 *pVB, const void *pvSrc, int cbSrc) +{ + HRESULT hr = D3D_OK; + void *pvDst = 0; + hr = pVB->Lock(0, 0, &pvDst, 0); + if (SUCCEEDED(hr)) + { + memcpy(pvDst, pvSrc, cbSrc); + hr = pVB->Unlock(); + } + return hr; +} + +struct Vertex +{ + float x, y; /* The vertex position. */ + float u, v; /* Texture coordinates. */ +}; + +typedef struct D3D9ConversionParameters +{ + DWORD const *paVS; /* Vertex shader code. */ + DWORD const *paPS; /* Pixel shader code. */ +} D3D9ConversionParameters; + +/** Select conversion shaders. + * @param d3dfmtFrom Source texture format. + * @param d3dfmtTo Target texture format. + * @param pResult Where the tore pointers to the shader code. + */ +static HRESULT d3d9SelectConversion(D3DFORMAT d3dfmtFrom, D3DFORMAT d3dfmtTo, + D3D9ConversionParameters *pResult) +{ + /* + * The shader code has been obtained from the hex listing file (hexdump.txt) produced by fxc HLSL compiler: + * fxc.exe /Op /Tfx_2_0 /Fxhexdump.txt shader.fx + * + * The vertex shader code is the same for all convestion variants. + * + * For example here is the complete code for aPSCodeSwapRB: + + uniform extern float4 gTextureInfo; // .xy = (width, height) in pixels, .zw = (1/width, 1/height) + uniform extern texture gTexSource; + sampler sSource = sampler_state + { + Texture = <gTexSource>; + }; + + struct VS_INPUT + { + float2 Position : POSITION; // In pixels. + float2 TexCoord : TEXCOORD0; + }; + + struct VS_OUTPUT + { + float4 Position : POSITION; // Normalized. + float2 TexCoord : TEXCOORD0; + }; + + VS_OUTPUT VS(VS_INPUT In) + { + VS_OUTPUT Output; + + // Position is in pixels, i.e [0; width - 1]. Top, left is 0,0. + // Convert to the normalized coords in the -1;1 range + float4 Position; + Position.x = 2.0f * In.Position.x / (gTextureInfo.x - 1.0f) - 1.0f; + Position.y = -2.0f * In.Position.y / (gTextureInfo.y - 1.0f) + 1.0f; + Position.z = 0.0f; // Not used. + Position.w = 1.0f; // It is a point. + + Output.Position = Position; + Output.TexCoord = In.TexCoord; + + return Output; + } + + struct PS_OUTPUT + { + float4 Color : COLOR0; + }; + + PS_OUTPUT PS(VS_OUTPUT In) + { + PS_OUTPUT Output; + + float2 texCoord = In.TexCoord; + + float4 texColor = tex2D(sSource, texCoord); + + Output.Color = texColor.bgra; // Swizzle rgba -> bgra + + return Output; + } + + technique RenderScene + { + pass P0 + { + VertexShader = compile vs_2_0 VS(); + PixelShader = compile ps_2_0 PS(); + } + } + */ + + static DWORD const aVSCode[] = + { + 0xfffe0200, // vs_2_0 + 0x05000051, 0xa00f0001, 0xbf800000, 0xc0000000, 0x3f800000, 0x00000000, // def c1, -1, -2, 1, 0 + 0x0200001f, 0x80000000, 0x900f0000, // dcl_position v0 + 0x0200001f, 0x80000005, 0x900f0001, // dcl_texcoord v1 + 0x03000002, 0x80010000, 0x90000000, 0x90000000, // add r0.x, v0.x, v0.x + 0x02000001, 0x80010001, 0xa0000001, // mov r1.x, c1.x + 0x03000002, 0x80060000, 0x80000001, 0xa0d00000, // add r0.yz, r1.x, c0.xxyw + 0x02000006, 0x80020000, 0x80550000, // rcp r0.y, r0.y + 0x02000006, 0x80040000, 0x80aa0000, // rcp r0.z, r0.z + 0x04000004, 0xc0010000, 0x80000000, 0x80550000, 0xa0000001, // mad oPos.x, r0.x, r0.y, c1.x + 0x03000005, 0x80010000, 0x90550000, 0xa0550001, // mul r0.x, v0.y, c1.y + 0x04000004, 0xc0020000, 0x80000000, 0x80aa0000, 0xa0aa0001, // mad oPos.y, r0.x, r0.z, c1.z + 0x02000001, 0xc00c0000, 0xa0b40001, // mov oPos.zw, c1.xywz + 0x02000001, 0xe0030000, 0x90e40001, // mov oT0.xy, v1 + 0x0000ffff + }; + + /* + * Swap R and B components. Converts D3DFMT_A8R8G8B8 <-> D3DFMT_A8B8G8R8. + */ + static DWORD const aPSCodeSwapRB[] = + { + 0xffff0200, // ps_2_0 + 0x0200001f, 0x80000000, 0xb0030000, // dcl t0.xy + 0x0200001f, 0x90000000, 0xa00f0800, // dcl_2d s0 + 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, // texld r0, t0, s0 + 0x02000001, 0x80090001, 0x80d20000, // mov r1.xw, r0.zxyw + 0x02000001, 0x80040001, 0x80000000, // mov r1.z, r0.x + 0x02000001, 0x80020001, 0x80550000, // mov r1.y, r0.y + 0x02000001, 0x800f0800, 0x80e40001, // mov oC0, r1 + 0x0000ffff + }; + + /* YUY2 to RGB: + + // YUY2 if not defined + // #define UYVY + + static const float3x3 yuvCoeffs = + { + 1.164383f, 1.164383f, 1.164383f, + 0.0f, -0.391762f, 2.017232f, + 1.596027f, -0.812968f, 0.0f + }; + + PS_OUTPUT PS(VS_OUTPUT In) + { + PS_OUTPUT Output; + + // 4 bytes of an YUV macropixel contain 2 pixels in X for the target. + // I.e. each YUV texture pixel is sampled twice: for both even and odd target pixels. + + // In.TexCoord are in [0;1] range for the target. + float2 texCoord = In.TexCoord; + + // Convert to the target coords in pixels: xPixel = TexCoord.x * Width. + float xTargetPixel = texCoord.x * gTextureInfo.x; + + // Source texture is half width, i.e. it contains data in pixels [0; width / 2 - 1]. + float xSourcePixel = xTargetPixel / 2.0f; + + // Remainder is about 0.25 for even pixels and about 0.75 for odd pixels. + float remainder = xSourcePixel - trunc(xSourcePixel); + + // Back to the normalized coords: texCoord.x = xPixel / Width. + texCoord.x = xSourcePixel * gTextureInfo.z; + + // Fetch YUV + float4 texColor = tex2D(sSource, texCoord); + + // Get YUV components. + #ifdef UYVY + float u = texColor.b; + float y0 = texColor.g; + float v = texColor.r; + float y1 = texColor.a; + #else // YUY2 + float y0 = texColor.b; + float u = texColor.g; + float y1 = texColor.r; + float v = texColor.a; + #endif + + // Get y0 for even x coordinates and y1 for odd ones. + float y = remainder < 0.5f ? y0 : y1; + + // Make a vector for easier calculation. + float3 yuv = float3(y, u, v); + + // Convert YUV to RGB: + // https://docs.microsoft.com/en-us/windows/win32/medfound/recommended-8-bit-yuv-formats-for-video-rendering#converting-8-bit-yuv-to-rgb888 + // + // For 8bit [0;255] when Y = [16;235], U,V = [16;239]: + // + // C = Y - 16 + // D = U - 128 + // E = V - 128 + // + // R = 1.164383 * C + 1.596027 * E + // G = 1.164383 * C - 0.391762 * D - 0.812968 * E + // B = 1.164383 * C + 2.017232 * D + // + // For shader values [0;1.0] when Y = [16/255;235/255], U,V = [16/255;239/255]: + // + // C = Y - 0.0627 + // D = U - 0.5020 + // E = V - 0.5020 + // + // R = 1.164383 * C + 1.596027 * E + // G = 1.164383 * C - 0.391762 * D - 0.812968 * E + // B = 1.164383 * C + 2.017232 * D + // + yuv -= float3(0.0627f, 0.502f, 0.502f); + float3 bgr = mul(yuv, yuvCoeffs); + + // Clamp to [0;1] + bgr = saturate(bgr); + + // Return RGBA + Output.Color = float4(bgr, 1.0f); + + return Output; + } + */ + static DWORD const aPSCodeYUY2toRGB[] = + { + 0xffff0200, // ps_2_0 + 0x05000051, 0xa00f0001, 0x3f000000, 0x00000000, 0x3f800000, 0xbf000000, // def c1, 0.5, 0, 1, -0.5 + 0x05000051, 0xa00f0002, 0xbd8068dc, 0xbf008312, 0xbf008312, 0x00000000, // def c2, -0.0627000034, -0.501999974, -0.501999974, 0 + 0x05000051, 0xa00f0003, 0x3f950a81, 0x00000000, 0x3fcc4a9d, 0x00000000, // def c3, 1.16438305, 0, 1.59602702, 0 + 0x05000051, 0xa00f0004, 0x3f950a81, 0xbec89507, 0xbf501eac, 0x00000000, // def c4, 1.16438305, -0.391761988, -0.812968016, 0 + 0x05000051, 0xa00f0005, 0x3f950a81, 0x40011a54, 0x00000000, 0x00000000, // def c5, 1.16438305, 2.01723194, 0, 0 + 0x0200001f, 0x80000000, 0xb0030000, // dcl t0.xy + 0x0200001f, 0x90000000, 0xa00f0800, // dcl_2d s0 + 0x03000005, 0x80080000, 0xb0000000, 0xa0000000, // mul r0.w, t0.x, c0.x + 0x03000005, 0x80010000, 0x80ff0000, 0xa0000001, // mul r0.x, r0.w, c1.x + 0x02000013, 0x80020000, 0x80000000, // frc r0.y, r0.x + 0x04000058, 0x80040000, 0x81550000, 0xa0550001, 0xa0aa0001, // cmp r0.z, -r0.y, c1.y, c1.z + 0x03000002, 0x80020000, 0x80000000, 0x81550000, // add r0.y, r0.x, -r0.y + 0x03000005, 0x80010001, 0x80000000, 0xa0aa0000, // mul r1.x, r0.x, c0.z + 0x04000058, 0x80010000, 0x80ff0000, 0xa0550001, 0x80aa0000, // cmp r0.x, r0.w, c1.y, r0.z + 0x03000002, 0x80010000, 0x80000000, 0x80550000, // add r0.x, r0.x, r0.y + 0x04000004, 0x80010000, 0x80ff0000, 0xa0000001, 0x81000000, // mad r0.x, r0.w, c1.x, -r0.x + 0x03000002, 0x80010000, 0x80000000, 0xa0ff0001, // add r0.x, r0.x, c1.w + 0x02000001, 0x80020001, 0xb0550000, // mov r1.y, t0.y + 0x03000042, 0x800f0001, 0x80e40001, 0xa0e40800, // texld r1, r1, s0 + 0x04000058, 0x80010001, 0x80000000, 0x80000001, 0x80aa0001, // cmp r1.x, r0.x, r1.x, r1.z + 0x02000001, 0x80040001, 0x80ff0001, // mov r1.z, r1.w + 0x03000002, 0x80070000, 0x80e40001, 0xa0e40002, // add r0.xyz, r1, c2 + 0x03000008, 0x80110001, 0x80e40000, 0xa0e40003, // dp3_sat r1.x, r0, c3 + 0x03000008, 0x80120001, 0x80e40000, 0xa0e40004, // dp3_sat r1.y, r0, c4 + 0x0400005a, 0x80140001, 0x80e40000, 0xa0e40005, 0xa0aa0005, // dp2add_sat r1.z, r0, c5, c5.z + 0x02000001, 0x80080001, 0xa0aa0001, // mov r1.w, c1.z + 0x02000001, 0x800f0800, 0x80e40001, // mov oC0, r1 + 0x0000ffff + }; + + /* UYVY to RGB is same as YUY2 above except for the order of yuv components: + + // YUY2 if not defined + #define UYVY + ... + */ + static DWORD const aPSCodeUYVYtoRGB[] = + { + 0xffff0200, // ps_2_0 + 0x05000051, 0xa00f0001, 0x3f000000, 0x00000000, 0x3f800000, 0xbf000000, // def c1, 0.5, 0, 1, -0.5 + 0x05000051, 0xa00f0002, 0xbd8068dc, 0xbf008312, 0xbf008312, 0x00000000, // def c2, -0.0627000034, -0.501999974, -0.501999974, 0 + 0x05000051, 0xa00f0003, 0x3f950a81, 0x00000000, 0x3fcc4a9d, 0x00000000, // def c3, 1.16438305, 0, 1.59602702, 0 + 0x05000051, 0xa00f0004, 0x3f950a81, 0xbec89507, 0xbf501eac, 0x00000000, // def c4, 1.16438305, -0.391761988, -0.812968016, 0 + 0x05000051, 0xa00f0005, 0x3f950a81, 0x40011a54, 0x00000000, 0x00000000, // def c5, 1.16438305, 2.01723194, 0, 0 + 0x0200001f, 0x80000000, 0xb0030000, // dcl t0.xy + 0x0200001f, 0x90000000, 0xa00f0800, // dcl_2d s0 + 0x03000005, 0x80080000, 0xb0000000, 0xa0000000, // mul r0.w, t0.x, c0.x + 0x03000005, 0x80010000, 0x80ff0000, 0xa0000001, // mul r0.x, r0.w, c1.x + 0x02000013, 0x80020000, 0x80000000, // frc r0.y, r0.x + 0x04000058, 0x80040000, 0x81550000, 0xa0550001, 0xa0aa0001, // cmp r0.z, -r0.y, c1.y, c1.z + 0x03000002, 0x80020000, 0x80000000, 0x81550000, // add r0.y, r0.x, -r0.y + 0x03000005, 0x80010001, 0x80000000, 0xa0aa0000, // mul r1.x, r0.x, c0.z + 0x04000058, 0x80010000, 0x80ff0000, 0xa0550001, 0x80aa0000, // cmp r0.x, r0.w, c1.y, r0.z + 0x03000002, 0x80010000, 0x80000000, 0x80550000, // add r0.x, r0.x, r0.y + 0x04000004, 0x80010000, 0x80ff0000, 0xa0000001, 0x81000000, // mad r0.x, r0.w, c1.x, -r0.x + 0x03000002, 0x80010000, 0x80000000, 0xa0ff0001, // add r0.x, r0.x, c1.w + 0x02000001, 0x80020001, 0xb0550000, // mov r1.y, t0.y + 0x03000042, 0x800f0001, 0x80e40001, 0xa0e40800, // texld r1, r1, s0 + 0x04000058, 0x80010000, 0x80000000, 0x80ff0001, 0x80550001, // cmp r0.x, r0.x, r1.w, r1.y + 0x02000001, 0x80060000, 0x80c90001, // mov r0.yz, r1.yzxw + 0x03000002, 0x80070000, 0x80e40000, 0xa0e40002, // add r0.xyz, r0, c2 + 0x03000008, 0x80110001, 0x80e40000, 0xa0e40003, // dp3_sat r1.x, r0, c3 + 0x03000008, 0x80120001, 0x80e40000, 0xa0e40004, // dp3_sat r1.y, r0, c4 + 0x0400005a, 0x80140001, 0x80e40000, 0xa0e40005, 0xa0aa0005, // dp2add_sat r1.z, r0, c5, c5.z + 0x02000001, 0x80080001, 0xa0aa0001, // mov r1.w, c1.z + 0x02000001, 0x800f0800, 0x80e40001, // mov oC0, r1 + 0x0000ffff + }; + + /* RGB to YUY2. + * UYVY is not defined. + + static const float3x3 bgrCoeffs = + { + 0.0977f, 0.4375f, -0.0703f, + 0.5039f, -0.2891f, -0.3672f, + 0.2578f, -0.1484f, 0.4375f + }; + + static const float3 yuvShift = { 0.0647f, 0.5039f, 0.5039f }; + + PS_OUTPUT PS(VS_OUTPUT In) + { + PS_OUTPUT Output; + + // 4 bytes of an YUV macropixel contain 2 source pixels in X. + // I.e. each YUV texture target pixel is computed from 2 source pixels. + // The target texture pixels are located in the [0; width / 2 - 1] range. + + // In.TexCoord are in [0;1] range, applicable both to the source and the target textures. + float2 texCoordDst = In.TexCoord; + + // Convert to the target coords in pixels: xPixel = TexCoord.x * Width. + float xTargetPixel = texCoordDst.x * gTextureInfo.x; + + float4 bgraOutputPixel; + if (xTargetPixel < gTextureInfo.x / 2.0f) + { + // Target texture is half width, i.e. it contains data in pixels [0; width / 2 - 1]. + // Compute the source texture coords for the pixels which will be used to compute the target pixel. + float2 texCoordSrc = texCoordDst; + texCoordSrc.x *= 2.0f; + + // Even pixel. Fetch two BGRA source pixels. + float4 texColor0 = tex2D(sSource, texCoordSrc); + + // Advance one pixel (+ 1/Width) + texCoordSrc.x += gTextureInfo.z; + float4 texColor1 = tex2D(sSource, texCoordSrc); + + // Compute y0, u, y1, v components + // https://docs.microsoft.com/en-us/windows/win32/medfound/recommended-8-bit-yuv-formats-for-video-rendering#converting-rgb888-to-yuv-444 + // + // For R,G,B and Y,U,V in [0;255] + // Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16 + // U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128 + // V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128 + // + // For r,g,b and y,u,v in [0;1.0] + // y = 0.2578 * r + 0.5039 * g + 0.0977 * b + 0.0647 + // u = -0.1484 * r - 0.2891 * g + 0.4375 * b + 0.5039 + // v = 0.4375 * r - 0.3672 * g - 0.0703 * b + 0.5039 + + float3 yuv0 = mul(texColor0.bgr, bgrCoeffs); + yuv0 -= yuvShift; + + float3 yuv1 = mul(texColor1.bgr, bgrCoeffs); + yuv1 -= yuvShift; + + float y0 = yuv0.b; + float u = (yuv0.g + yuv1.g) / 2.0f; + float y1 = yuv1.b; + float v = (yuv0.r + yuv1.r) / 2.0f; + + #ifdef UYVY + bgraOutputPixel = float4(u, y0, v, y1); + #else // YUY2 + bgraOutputPixel = float4(y0, u, y1, v); + #endif + } + else + { + // [width / 2; width - 1] pixels are not used. Set to something. + bgraOutputPixel = float4(0.0f, 0.0f, 0.0f, 0.0f); + } + + // Clamp to [0;1] + bgraOutputPixel = saturate(bgraOutputPixel); + + // Return RGBA + Output.Color = bgraOutputPixel; + + return Output; + } + */ + static DWORD const aPSCodeRGBtoYUY2[] = + { + 0xffff0200, // ps_2_0 + 0x05000051, 0xa00f0001, 0xbd84816f, 0xbf00ff97, 0xbf00ff97, 0x00000000, // def c1, -0.0647, -0.503899992, -0.503899992, 0 + 0x05000051, 0xa00f0002, 0xbe80ff97, 0x00000000, 0xbd04816f, 0x00000000, // def c2, -0.251949996, 0, -0.03235, 0 + 0x05000051, 0xa00f0003, 0x3dc816f0, 0x3f00ff97, 0x3e83fe5d, 0x00000000, // def c3, 0.0976999998, 0.503899992, 0.257800013, 0 + 0x05000051, 0xa00f0004, 0x3ee00000, 0xbe9404ea, 0xbe17f62b, 0x00000000, // def c4, 0.4375, -0.289099991, -0.148399994, 0 + 0x05000051, 0xa00f0005, 0xbd8ff972, 0xbebc01a3, 0x3ee00000, 0x00000000, // def c5, -0.0702999979, -0.367199987, 0.4375, 0 + 0x05000051, 0xa00f0006, 0x3f000000, 0x40000000, 0x3f800000, 0xbf00ff97, // def c6, 0.5, 2, 1, -0.503899992 + 0x05000051, 0xa00f0007, 0x3f000000, 0x3f800000, 0x3f000000, 0x00000000, // def c7, 0.5, 1, 0.5, 0 + 0x0200001f, 0x80000000, 0xb0030000, // dcl t0.xy + 0x0200001f, 0x90000000, 0xa00f0800, // dcl_2d s0 + 0x03000005, 0x80030000, 0xb0e40000, 0xa0c90006, // mul r0.xy, t0, c6.yzxw + 0x02000001, 0x80030001, 0xa0e40006, // mov r1.xy, c6 + 0x04000004, 0x80010002, 0xb0000000, 0x80550001, 0xa0aa0000, // mad r2.x, t0.x, r1.y, c0.z + 0x02000001, 0x80020002, 0xb0550000, // mov r2.y, t0.y + 0x03000042, 0x800f0000, 0x80e40000, 0xa0e40800, // texld r0, r0, s0 + 0x03000042, 0x800f0002, 0x80e40002, 0xa0e40800, // texld r2, r2, s0 + 0x03000005, 0x80080000, 0x80aa0000, 0xa0000003, // mul r0.w, r0.z, c3.x + 0x04000004, 0x80080000, 0x80550000, 0xa0550003, 0x80ff0000, // mad r0.w, r0.y, c3.y, r0.w + 0x04000004, 0x80010003, 0x80000000, 0xa0aa0003, 0x80ff0000, // mad r3.x, r0.x, c3.z, r0.w + 0x03000005, 0x80080000, 0x80aa0000, 0xa0000004, // mul r0.w, r0.z, c4.x + 0x04000004, 0x80080000, 0x80550000, 0xa0550004, 0x80ff0000, // mad r0.w, r0.y, c4.y, r0.w + 0x04000004, 0x80020003, 0x80000000, 0xa0aa0004, 0x80ff0000, // mad r3.y, r0.x, c4.z, r0.w + 0x03000005, 0x80080002, 0x80aa0000, 0xa0000005, // mul r2.w, r0.z, c5.x + 0x04000004, 0x80080002, 0x80550000, 0xa0550005, 0x80ff0002, // mad r2.w, r0.y, c5.y, r2.w + 0x04000004, 0x80040003, 0x80000000, 0xa0aa0005, 0x80ff0002, // mad r3.z, r0.x, c5.z, r2.w + 0x03000002, 0x80070000, 0x80e40003, 0xa0e40001, // add r0.xyz, r3, c1 + 0x02000001, 0x80080000, 0xa0ff0006, // mov r0.w, c6.w + 0x03000005, 0x80080002, 0x80aa0002, 0xa0000003, // mul r2.w, r2.z, c3.x + 0x04000004, 0x80080002, 0x80550002, 0xa0550003, 0x80ff0002, // mad r2.w, r2.y, c3.y, r2.w + 0x04000004, 0x80040003, 0x80000002, 0xa0aa0003, 0x80ff0002, // mad r3.z, r2.x, c3.z, r2.w + 0x03000005, 0x80080002, 0x80aa0002, 0xa0000004, // mul r2.w, r2.z, c4.x + 0x04000004, 0x80080002, 0x80550002, 0xa0550004, 0x80ff0002, // mad r2.w, r2.y, c4.y, r2.w + 0x04000004, 0x80010003, 0x80000002, 0xa0aa0004, 0x80ff0002, // mad r3.x, r2.x, c4.z, r2.w + 0x03000005, 0x80080003, 0x80aa0002, 0xa0000005, // mul r3.w, r2.z, c5.x + 0x04000004, 0x80080003, 0x80550002, 0xa0550005, 0x80ff0003, // mad r3.w, r2.y, c5.y, r3.w + 0x04000004, 0x80020003, 0x80000002, 0xa0aa0005, 0x80ff0003, // mad r3.y, r2.x, c5.z, r3.w + 0x03000002, 0x80050002, 0x80c90000, 0x80e40003, // add r2.xz, r0.yzxw, r3 + 0x03000002, 0x80020002, 0x80ff0000, 0x80550003, // add r2.y, r0.w, r3.y + 0x02000001, 0x80110000, 0x80aa0000, // mov_sat r0.x, r0.z + 0x02000001, 0x80070003, 0xa0e40007, // mov r3.xyz, c7 + 0x04000004, 0x80160000, 0x80d20002, 0x80d20003, 0xa0d20002, // mad_sat r0.yz, r2.zxyw, r3.zxyw, c2.zxyw + 0x04000004, 0x80180000, 0x80aa0002, 0x80aa0003, 0xa0aa0002, // mad_sat r0.w, r2.z, r3.z, c2.z + 0x03000005, 0x80010001, 0x80000001, 0xa0000000, // mul r1.x, r1.x, c0.x + 0x04000004, 0x80010001, 0xb0000000, 0xa0000000, 0x81000001, // mad r1.x, t0.x, c0.x, -r1.x + 0x04000058, 0x800f0000, 0x80000001, 0xa0ff0003, 0x80e40000, // cmp r0, r1.x, c3.w, r0 + 0x02000001, 0x800f0800, 0x80e40000, // mov oC0, r0 + 0x0000ffff + }; + + /* RGB to UYVY is same as YUY2 above except for the order of yuv components. + * UYVY is defined. + */ + static DWORD const aPSCodeRGBtoUYVY[] = + { + 0xffff0200, // ps_2_0 + 0x05000051, 0xa00f0001, 0xbd84816f, 0xbf00ff97, 0xbf00ff97, 0x00000000, // def c1, -0.0647, -0.503899992, -0.503899992, 0 + 0x05000051, 0xa00f0002, 0xbe80ff97, 0xbd04816f, 0x00000000, 0x00000000, // def c2, -0.251949996, -0.03235, 0, 0 + 0x05000051, 0xa00f0003, 0x3dc816f0, 0x3f00ff97, 0x3e83fe5d, 0x00000000, // def c3, 0.0976999998, 0.503899992, 0.257800013, 0 + 0x05000051, 0xa00f0004, 0x3ee00000, 0xbe9404ea, 0xbe17f62b, 0x00000000, // def c4, 0.4375, -0.289099991, -0.148399994, 0 + 0x05000051, 0xa00f0005, 0xbd8ff972, 0xbebc01a3, 0x3ee00000, 0x00000000, // def c5, -0.0702999979, -0.367199987, 0.4375, 0 + 0x05000051, 0xa00f0006, 0x3f000000, 0x40000000, 0x3f800000, 0xbf00ff97, // def c6, 0.5, 2, 1, -0.503899992 + 0x05000051, 0xa00f0007, 0x3f000000, 0x3f000000, 0x3f800000, 0x00000000, // def c7, 0.5, 0.5, 1, 0 + 0x0200001f, 0x80000000, 0xb0030000, // dcl t0.xy + 0x0200001f, 0x90000000, 0xa00f0800, // dcl_2d s0 + 0x03000005, 0x80030000, 0xb0e40000, 0xa0c90006, // mul r0.xy, t0, c6.yzxw + 0x02000001, 0x80030001, 0xa0e40006, // mov r1.xy, c6 + 0x04000004, 0x80010002, 0xb0000000, 0x80550001, 0xa0aa0000, // mad r2.x, t0.x, r1.y, c0.z + 0x02000001, 0x80020002, 0xb0550000, // mov r2.y, t0.y + 0x03000042, 0x800f0000, 0x80e40000, 0xa0e40800, // texld r0, r0, s0 + 0x03000042, 0x800f0002, 0x80e40002, 0xa0e40800, // texld r2, r2, s0 + 0x03000005, 0x80080000, 0x80aa0000, 0xa0000003, // mul r0.w, r0.z, c3.x + 0x04000004, 0x80080000, 0x80550000, 0xa0550003, 0x80ff0000, // mad r0.w, r0.y, c3.y, r0.w + 0x04000004, 0x80010003, 0x80000000, 0xa0aa0003, 0x80ff0000, // mad r3.x, r0.x, c3.z, r0.w + 0x03000005, 0x80080000, 0x80aa0000, 0xa0000004, // mul r0.w, r0.z, c4.x + 0x04000004, 0x80080000, 0x80550000, 0xa0550004, 0x80ff0000, // mad r0.w, r0.y, c4.y, r0.w + 0x04000004, 0x80020003, 0x80000000, 0xa0aa0004, 0x80ff0000, // mad r3.y, r0.x, c4.z, r0.w + 0x03000005, 0x80080002, 0x80aa0000, 0xa0000005, // mul r2.w, r0.z, c5.x + 0x04000004, 0x80080002, 0x80550000, 0xa0550005, 0x80ff0002, // mad r2.w, r0.y, c5.y, r2.w + 0x04000004, 0x80040003, 0x80000000, 0xa0aa0005, 0x80ff0002, // mad r3.z, r0.x, c5.z, r2.w + 0x03000002, 0x80070000, 0x80e40003, 0xa0e40001, // add r0.xyz, r3, c1 + 0x02000001, 0x80080000, 0xa0ff0006, // mov r0.w, c6.w + 0x03000005, 0x80080002, 0x80aa0002, 0xa0000003, // mul r2.w, r2.z, c3.x + 0x04000004, 0x80080002, 0x80550002, 0xa0550003, 0x80ff0002, // mad r2.w, r2.y, c3.y, r2.w + 0x04000004, 0x80020003, 0x80000002, 0xa0aa0003, 0x80ff0002, // mad r3.y, r2.x, c3.z, r2.w + 0x03000005, 0x80080002, 0x80aa0002, 0xa0000004, // mul r2.w, r2.z, c4.x + 0x04000004, 0x80080002, 0x80550002, 0xa0550004, 0x80ff0002, // mad r2.w, r2.y, c4.y, r2.w + 0x04000004, 0x80010003, 0x80000002, 0xa0aa0004, 0x80ff0002, // mad r3.x, r2.x, c4.z, r2.w + 0x03000005, 0x80080003, 0x80aa0002, 0xa0000005, // mul r3.w, r2.z, c5.x + 0x04000004, 0x80080003, 0x80550002, 0xa0550005, 0x80ff0003, // mad r3.w, r2.y, c5.y, r3.w + 0x04000004, 0x80040003, 0x80000002, 0xa0aa0005, 0x80ff0003, // mad r3.z, r2.x, c5.z, r3.w + 0x03000002, 0x80010002, 0x80550000, 0x80000003, // add r2.x, r0.y, r3.x + 0x03000002, 0x80020002, 0x80000000, 0x80550003, // add r2.y, r0.x, r3.y + 0x03000002, 0x80040002, 0x80ff0000, 0x80aa0003, // add r2.z, r0.w, r3.z + 0x02000001, 0x80120000, 0x80aa0000, // mov_sat r0.y, r0.z + 0x02000001, 0x80070003, 0xa0e40007, // mov r3.xyz, c7 + 0x04000004, 0x80110000, 0x80000002, 0x80000003, 0xa0000002, // mad_sat r0.x, r2.x, r3.x, c2.x + 0x04000004, 0x80140000, 0x80550002, 0x80550003, 0xa0550002, // mad_sat r0.z, r2.y, r3.y, c2.y + 0x04000004, 0x80180000, 0x80aa0002, 0x80aa0003, 0xa0aa0002, // mad_sat r0.w, r2.z, r3.z, c2.z + 0x03000005, 0x80010001, 0x80000001, 0xa0000000, // mul r1.x, r1.x, c0.x + 0x04000004, 0x80010001, 0xb0000000, 0xa0000000, 0x81000001, // mad r1.x, t0.x, c0.x, -r1.x + 0x04000058, 0x800f0000, 0x80000001, 0xa0ff0003, 0x80e40000, // cmp r0, r1.x, c3.w, r0 + 0x02000001, 0x800f0800, 0x80e40000, // mov oC0, r0 + 0x0000ffff + }; + + switch (d3dfmtFrom) + { + /* + * Emulated to ARGB + */ + case D3DFMT_A8B8G8R8: + { + if (d3dfmtTo == D3DFMT_A8R8G8B8) + { + pResult->paVS = aVSCode; + pResult->paPS = aPSCodeSwapRB; + + return D3D_OK; + } + } break; + case D3DFMT_UYVY: + { + if (d3dfmtTo == D3DFMT_A8R8G8B8) + { + pResult->paVS = aVSCode; + pResult->paPS = aPSCodeUYVYtoRGB; + + return D3D_OK; + } + } break; + case D3DFMT_YUY2: + { + if (d3dfmtTo == D3DFMT_A8R8G8B8) + { + pResult->paVS = aVSCode; + pResult->paPS = aPSCodeYUY2toRGB; + + return D3D_OK; + } + } break; + + /* + * ARGB to emulated. + */ + case D3DFMT_A8R8G8B8: + { + if (d3dfmtTo == D3DFMT_A8B8G8R8) + { + pResult->paVS = aVSCode; + pResult->paPS = aPSCodeSwapRB; + + return D3D_OK; + } + else if (d3dfmtTo == D3DFMT_UYVY) + { + pResult->paVS = aVSCode; + pResult->paPS = aPSCodeRGBtoUYVY; + + return D3D_OK; + } + else if (d3dfmtTo == D3DFMT_YUY2) + { + pResult->paVS = aVSCode; + pResult->paPS = aPSCodeRGBtoYUY2; + + return D3D_OK; + } + } break; + + default: + break; + } + + return E_NOTIMPL; +} + +class D3D9Conversion +{ + private: + IDirect3DDevice9Ex *mpDevice; + + /* State objects. */ + IDirect3DVertexBuffer9 *mpVB; + IDirect3DVertexDeclaration9 *mpVertexDecl; + IDirect3DVertexShader9 *mpVS; + IDirect3DPixelShader9 *mpPS; + + D3D9ConversionParameters mParameters; + + typedef struct D3DSamplerState + { + D3DSAMPLERSTATETYPE Type; + DWORD Value; + } D3DSamplerState; + + /* Saved context. */ + struct + { + DWORD dwCull; + DWORD dwZEnable; + IDirect3DSurface9 *pRT; + IDirect3DVertexShader9 *pVS; + IDirect3DPixelShader9 *pPS; + IDirect3DBaseTexture9 *pTexture; + float aVSConstantData[4]; + float aPSConstantData[4]; + D3DSamplerState aSamplerState[3]; + } mSaved; + + void destroyConversion(); + HRESULT saveContextState(); + HRESULT restoreContextState(PVMSVGA3DCONTEXT pContext); + HRESULT initConversion(); + HRESULT setConversionState(IDirect3DTexture9 *pSourceTexture, + uint32_t cWidth, + uint32_t cHeight); + + public: + enum Direction + { + FromEmulated, + ToEmulated, + }; + + D3D9Conversion(IDirect3DDevice9Ex *pDevice); + ~D3D9Conversion(); + + HRESULT SelectConversion(D3DFORMAT d3dfmtFrom, D3DFORMAT d3dfmtTo); + HRESULT ConvertTexture(PVMSVGA3DCONTEXT pContext, + PVMSVGA3DSURFACE pSurface, + Direction enmDirection); +}; + +D3D9Conversion::D3D9Conversion(IDirect3DDevice9Ex *pDevice) + : + mpDevice(pDevice), + mpVB(0), + mpVertexDecl(0), + mpVS(0), + mpPS(0) +{ + mParameters.paVS = 0; + mParameters.paPS = 0; + mSaved.dwCull = D3DCULL_NONE; + mSaved.dwZEnable = D3DZB_FALSE; + mSaved.pRT = 0; + mSaved.pVS = 0; + mSaved.pPS = 0; + mSaved.pTexture = 0; + RT_ZERO(mSaved.aVSConstantData); + RT_ZERO(mSaved.aPSConstantData); + mSaved.aSamplerState[0].Type = D3DSAMP_MAGFILTER; + mSaved.aSamplerState[0].Value = D3DTEXF_POINT; + mSaved.aSamplerState[1].Type = D3DSAMP_MINFILTER; + mSaved.aSamplerState[1].Value = D3DTEXF_POINT; + mSaved.aSamplerState[2].Type = D3DSAMP_MIPFILTER; + mSaved.aSamplerState[2].Value = D3DTEXF_NONE; +} + +D3D9Conversion::~D3D9Conversion() +{ + destroyConversion(); +} + +void D3D9Conversion::destroyConversion() +{ + D3D_RELEASE(mpVertexDecl); + D3D_RELEASE(mpVB); + D3D_RELEASE(mpVS); + D3D_RELEASE(mpPS); +} + +HRESULT D3D9Conversion::saveContextState() +{ + /* + * This is probably faster than + * IDirect3DStateBlock9 *mpStateBlock; + * mpDevice->CreateStateBlock(D3DSBT_ALL, &mpStateBlock); + */ + HRESULT hr = mpDevice->GetRenderState(D3DRS_CULLMODE, &mSaved.dwCull); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->GetRenderState(D3DRS_ZENABLE, &mSaved.dwZEnable); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->GetRenderTarget(0, &mSaved.pRT); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->GetVertexShader(&mSaved.pVS); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->GetPixelShader(&mSaved.pPS); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->GetTexture(0, &mSaved.pTexture); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->GetVertexShaderConstantF(0, mSaved.aVSConstantData, 1); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->GetPixelShaderConstantF(0, mSaved.aPSConstantData, 1); + AssertReturn(hr == D3D_OK, hr); + + for (uint32_t i = 0; i < RT_ELEMENTS(mSaved.aSamplerState); ++i) + { + hr = mpDevice->GetSamplerState(0, mSaved.aSamplerState[i].Type, &mSaved.aSamplerState[i].Value); + AssertReturn(hr == D3D_OK, hr); + } + + return hr; +} + +HRESULT D3D9Conversion::restoreContextState(PVMSVGA3DCONTEXT pContext) +{ + HRESULT hr = mpDevice->SetRenderState(D3DRS_CULLMODE, mSaved.dwCull); + Assert(hr == D3D_OK); + + hr = mpDevice->SetRenderState(D3DRS_ZENABLE, mSaved.dwZEnable); + Assert(hr == D3D_OK); + + hr = mpDevice->SetRenderTarget(0, mSaved.pRT); + D3D_RELEASE(mSaved.pRT); /* GetRenderTarget increases the internal reference count. */ + Assert(hr == D3D_OK); + + hr = mpDevice->SetVertexDeclaration(pContext->d3dState.pVertexDecl); + Assert(hr == D3D_OK); + + hr = mpDevice->SetVertexShader(mSaved.pVS); + Assert(hr == D3D_OK); + + hr = mpDevice->SetPixelShader(mSaved.pPS); + Assert(hr == D3D_OK); + + hr = mpDevice->SetTexture(0, mSaved.pTexture); + D3D_RELEASE(mSaved.pTexture); /* GetTexture increases the internal reference count. */ + Assert(hr == D3D_OK); + + hr = mpDevice->SetVertexShaderConstantF(0, mSaved.aVSConstantData, 1); + Assert(hr == D3D_OK); + + hr = mpDevice->SetPixelShaderConstantF(0, mSaved.aPSConstantData, 1); + Assert(hr == D3D_OK); + + for (uint32_t i = 0; i < RT_ELEMENTS(mSaved.aSamplerState); ++i) + { + hr = mpDevice->SetSamplerState(0, mSaved.aSamplerState[i].Type, mSaved.aSamplerState[i].Value); + Assert(hr == D3D_OK); + } + + return hr; +} + +HRESULT D3D9Conversion::initConversion() +{ + static D3DVERTEXELEMENT9 const aVertexElements[] = + { + {0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + D3DDECL_END() + }; + + HRESULT hr = mpDevice->CreateVertexDeclaration(aVertexElements, &mpVertexDecl); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->CreateVertexBuffer(6 * sizeof(Vertex), + 0, /* D3DUSAGE_* */ + 0, /* FVF */ + D3DPOOL_DEFAULT, + &mpVB, + 0); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->CreateVertexShader(mParameters.paVS, &mpVS); + AssertReturn(hr == D3D_OK, hr); + + hr = mpDevice->CreatePixelShader(mParameters.paPS, &mpPS); + AssertReturn(hr == D3D_OK, hr); + + return hr; +} + +HRESULT D3D9Conversion::setConversionState(IDirect3DTexture9 *pSourceTexture, + uint32_t cWidth, + uint32_t cHeight) +{ + /* Subtract 0.5 to line up the pixel centers with texels + * https://docs.microsoft.com/en-us/windows/win32/direct3d9/directly-mapping-texels-to-pixels + */ + float const xLeft = -0.5f; + float const xRight = (float)(cWidth - 1) - 0.5f; + float const yTop = -0.5f; + float const yBottom = (float)(cHeight - 1) - 0.5f; + + Vertex const aVertices[] = + { + { xLeft, yTop, 0.0f, 0.0f}, + { xRight, yTop, 1.0f, 0.0f}, + { xRight, yBottom, 1.0f, 1.0f}, + + { xLeft, yTop, 0.0f, 0.0f}, + { xRight, yBottom, 1.0f, 1.0f}, + { xLeft, yBottom, 0.0f, 1.0f}, + }; + + HRESULT hr = d3dCopyToVertexBuffer(mpVB, aVertices, sizeof(aVertices)); + AssertReturn(hr == D3D_OK, hr); + + /* No need to save the stream source, because vmsvga3dDrawPrimitives always sets it. */ + hr = mpDevice->SetStreamSource(0, mpVB, 0, sizeof(Vertex)); + AssertReturn(hr == D3D_OK, hr); + + /* Stored in pContext->d3dState.pVertexDecl. */ + hr = mpDevice->SetVertexDeclaration(mpVertexDecl); + AssertReturn(hr == D3D_OK, hr); + + /* Saved by saveContextState. */ + hr = mpDevice->SetVertexShader(mpVS); + AssertReturn(hr == D3D_OK, hr); + + /* Saved by saveContextState. */ + hr = mpDevice->SetPixelShader(mpPS); + AssertReturn(hr == D3D_OK, hr); + + /* Saved by saveContextState. */ + hr = mpDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + AssertReturn(hr == D3D_OK, hr); + + /* Saved by saveContextState. */ + hr = mpDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); + AssertReturn(hr == D3D_OK, hr); + + /* Saved by saveContextState. */ + hr = mpDevice->SetTexture(0, pSourceTexture); + AssertReturn(hr == D3D_OK, hr); + + float aTextureInfo[4]; + aTextureInfo[0] = (float)cWidth; + aTextureInfo[1] = (float)cHeight; + aTextureInfo[2] = 1.0f / (float)cWidth; /* Pixel width in texture coords. */ + aTextureInfo[3] = 1.0f / (float)cHeight; /* Pixel height in texture coords. */ + + /* Saved by saveContextState. */ + hr = mpDevice->SetVertexShaderConstantF(0, aTextureInfo, 1); + AssertReturn(hr == D3D_OK, hr); + + /* Saved by saveContextState. */ + hr = mpDevice->SetPixelShaderConstantF(0, aTextureInfo, 1); + AssertReturn(hr == D3D_OK, hr); + + /* Saved by saveContextState. */ + hr = mpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + AssertReturn(hr == D3D_OK, hr); + hr = mpDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + AssertReturn(hr == D3D_OK, hr); + hr = mpDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE); + AssertReturn(hr == D3D_OK, hr); + + return hr; +} + + +HRESULT D3D9Conversion::SelectConversion(D3DFORMAT d3dfmtFrom, D3DFORMAT d3dfmtTo) +{ + /** @todo d3d9SelectConversion should be a member. Move the code here? */ + HRESULT hr = d3d9SelectConversion(d3dfmtFrom, d3dfmtTo, &mParameters); + return hr; +} + +HRESULT D3D9Conversion::ConvertTexture(PVMSVGA3DCONTEXT pContext, + PVMSVGA3DSURFACE pSurface, + Direction enmDirection) +{ + IDirect3DTexture9 *pSourceTexture; + IDirect3DTexture9 *pTargetTexture; + D3D9TextureType enmTargetType; + + if (enmDirection == FromEmulated) + { + pSourceTexture = pSurface->emulated.pTexture; + pTargetTexture = pSurface->u.pTexture; + enmTargetType = D3D9TextureType_Texture; + } + else if (enmDirection == ToEmulated) + { + pSourceTexture = pSurface->u.pTexture; + pTargetTexture = pSurface->emulated.pTexture; + enmTargetType = D3D9TextureType_Emulated; + } + else + AssertFailedReturn(E_INVALIDARG); + + AssertPtrReturn(pSourceTexture, E_INVALIDARG); + AssertPtrReturn(pTargetTexture, E_INVALIDARG); + + HRESULT hr = saveContextState(); + if (hr == D3D_OK) + { + hr = initConversion(); + if (hr == D3D_OK) + { + uint32_t const cWidth = pSurface->paMipmapLevels[0].mipmapSize.width; + uint32_t const cHeight = pSurface->paMipmapLevels[0].mipmapSize.height; + + hr = setConversionState(pSourceTexture, cWidth, cHeight); + if (hr == D3D_OK) + { + hr = mpDevice->BeginScene(); + Assert(hr == D3D_OK); + if (hr == D3D_OK) + { + for (DWORD iFace = 0; iFace < pSurface->cFaces; ++iFace) + { + DWORD const cMipLevels = pTargetTexture->GetLevelCount(); + for (DWORD iMipmap = 0; iMipmap < cMipLevels && hr == D3D_OK; ++iMipmap) + { + IDirect3DSurface9 *pRT = NULL; + hr = D3D9GetTextureLevel(pSurface, enmTargetType, iFace, iMipmap, &pRT); + Assert(hr == D3D_OK); + if (hr == D3D_OK) + { + hr = mpDevice->SetRenderTarget(0, pRT); + Assert(hr == D3D_OK); + if (hr == D3D_OK) + { + hr = mpDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2); + Assert(hr == D3D_OK); + } + + D3D_RELEASE(pRT); + } + } + } + + hr = mpDevice->EndScene(); + Assert(hr == D3D_OK); + } + } + } + + hr = restoreContextState(pContext); + } + + destroyConversion(); + + return hr; +} + + +HRESULT D3D9UpdateTexture(PVMSVGA3DCONTEXT pContext, + PVMSVGA3DSURFACE pSurface) +{ + HRESULT hr = S_OK; + + if (pSurface->formatD3D == pSurface->d3dfmtRequested) + { + hr = pContext->pDevice->UpdateTexture(pSurface->bounce.pTexture, pSurface->u.pTexture); + } + else + { + if (pSurface->enmD3DResType == VMSVGA3D_D3DRESTYPE_TEXTURE) + { + hr = pContext->pDevice->UpdateTexture(pSurface->bounce.pTexture, pSurface->emulated.pTexture); + if (hr == D3D_OK) + { + D3D9Conversion conv(pContext->pDevice); + hr = conv.SelectConversion(pSurface->d3dfmtRequested, pSurface->formatD3D); + if (hr == D3D_OK) + { + hr = conv.ConvertTexture(pContext, pSurface, D3D9Conversion::FromEmulated); + } + } + } + else + { + /** @todo Cubemaps and maybe volume textures. */ + hr = pContext->pDevice->UpdateTexture(pSurface->bounce.pTexture, pSurface->u.pTexture); + } + } + + return hr; +} + +HRESULT D3D9GetSurfaceLevel(PVMSVGA3DSURFACE pSurface, + uint32_t uFace, + uint32_t uMipmap, + bool fBounce, + IDirect3DSurface9 **ppD3DSurface) +{ + HRESULT hr; + + if ( pSurface->enmD3DResType == VMSVGA3D_D3DRESTYPE_CUBE_TEXTURE + || pSurface->enmD3DResType == VMSVGA3D_D3DRESTYPE_TEXTURE) + { + D3D9TextureType enmType; + if (fBounce) + enmType = D3D9TextureType_Bounce; + else if (pSurface->formatD3D != pSurface->d3dfmtRequested) + enmType = D3D9TextureType_Emulated; + else + enmType = D3D9TextureType_Texture; + + hr = D3D9GetTextureLevel(pSurface, enmType, uFace, uMipmap, ppD3DSurface); + } + else if (pSurface->enmD3DResType == VMSVGA3D_D3DRESTYPE_SURFACE) + { + pSurface->u.pSurface->AddRef(); + *ppD3DSurface = pSurface->u.pSurface; + hr = S_OK; + } + else + { + AssertMsgFailed(("No surface for type %d\n", pSurface->enmD3DResType)); + hr = E_INVALIDARG; + } + + return hr; +} + +/** Copy the texture content to the bounce texture. + */ +HRESULT D3D9GetRenderTargetData(PVMSVGA3DCONTEXT pContext, + PVMSVGA3DSURFACE pSurface, + uint32_t uFace, + uint32_t uMipmap) +{ + HRESULT hr; + + /* Get the corresponding bounce texture surface. */ + IDirect3DSurface9 *pDst = NULL; + hr = D3D9GetSurfaceLevel(pSurface, uFace, uMipmap, true, &pDst); + AssertReturn(hr == D3D_OK, hr); + + /* Get the actual texture surface, emulated or actual. */ + IDirect3DSurface9 *pSrc = NULL; + hr = D3D9GetSurfaceLevel(pSurface, uFace, uMipmap, false, &pSrc); + AssertReturnStmt(hr == D3D_OK, + D3D_RELEASE(pDst), hr); + + Assert(pDst != pSrc); + + if (pSurface->formatD3D == pSurface->d3dfmtRequested) + { + hr = pContext->pDevice->GetRenderTargetData(pSrc, pDst); + AssertMsg(hr == D3D_OK, ("GetRenderTargetData failed with %x\n", hr)); + } + else + { + D3D9Conversion conv(pContext->pDevice); + hr = conv.SelectConversion(pSurface->formatD3D, pSurface->d3dfmtRequested); + if (hr == D3D_OK) + { + hr = conv.ConvertTexture(pContext, pSurface, D3D9Conversion::ToEmulated); + } + + if (hr == D3D_OK) + { + hr = pContext->pDevice->GetRenderTargetData(pSrc, pDst); + AssertMsg(hr == D3D_OK, ("GetRenderTargetData failed with %x\n", hr)); + } + } + + D3D_RELEASE(pSrc); + D3D_RELEASE(pDst); + return hr; +} + +D3DFORMAT D3D9GetActualFormat(PVMSVGA3DSTATE pState, D3DFORMAT d3dfmtRequested) +{ + RT_NOREF(pState); + + switch (d3dfmtRequested) + { + case D3DFMT_UYVY: + if (!pState->fSupportedFormatUYVY) return D3DFMT_A8R8G8B8; + break; + case D3DFMT_YUY2: + if (!pState->fSupportedFormatYUY2) return D3DFMT_A8R8G8B8; + break; + case D3DFMT_A8B8G8R8: + if (!pState->fSupportedFormatA8B8G8R8) return D3DFMT_A8R8G8B8; + break; + default: + break; + } + + /* Use the requested format. No emulation required. */ + return d3dfmtRequested; +} + +bool D3D9CheckDeviceFormat(IDirect3D9 *pD3D9, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) +{ + HRESULT hr = pD3D9->CheckDeviceFormat(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + D3DFMT_X8R8G8B8, /* assume standard 32-bit display mode */ + Usage, + RType, + CheckFormat); + return (hr == D3D_OK); +} |