diff options
Diffstat (limited to 'src/VBox/Devices/Graphics/DevVGA-SVGA3d-glHlp.cpp')
-rw-r--r-- | src/VBox/Devices/Graphics/DevVGA-SVGA3d-glHlp.cpp | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/src/VBox/Devices/Graphics/DevVGA-SVGA3d-glHlp.cpp b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-glHlp.cpp new file mode 100644 index 00000000..82bc29b8 --- /dev/null +++ b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-glHlp.cpp @@ -0,0 +1,768 @@ +/* $Id: DevVGA-SVGA3d-glHlp.cpp $ */ +/** @file + * DevVMWare - VMWare SVGA device OpenGL backend + */ + +/* + * Copyright (C) 2013-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 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#ifdef DEBUG_bird +//# define RTMEM_WRAP_TO_EF_APIS +#endif +#define LOG_GROUP LOG_GROUP_DEV_VMSVGA +#include <VBox/vmm/pdmdev.h> + +#include <iprt/assert.h> +#include <iprt/mem.h> + +#include "DevVGA-SVGA.h" +#include "DevVGA-SVGA3d-internal.h" + +/* Parameters for glVertexAttribPointer. */ +typedef struct VertexAttribDesc +{ + GLint size; + GLenum type; + GLboolean normalized; + GLsizei stride; + GLsizei offset; +} VertexAttribDesc; + +/* Information about a shader program. */ +typedef struct ShaderProgram +{ + GLuint vertexShader; /* Vertex shader name. */ + GLuint fragmentShader; /* Fragment shader name. */ + GLuint program; /* Shader program name. */ + GLint sSourceTex; /* Location of the texture sampler uniform in the shader. */ + GLint uTexInfo; /* Location of the texture information uniform in the shader. */ +} ShaderProgram; + +/* Texture format conversion data. + * Uses a fragment (pixel) shader to render a source texture in one format + * to the target texture in another format. + */ +typedef struct VMSVGA3DFORMATCONVERTER +{ + PVMSVGA3DSTATE pState; + + ShaderProgram programYUY2ToRGB; /* From the YUY2 emulated format to the actual RGB texture. */ + ShaderProgram programYUY2FromRGB; /* From the actual RGB texture to the emulated YUY2 format. */ + ShaderProgram programUYVYToRGB; /* From the UYVY emulated format to the actual RGB texture. */ + ShaderProgram programUYVYFromRGB; /* From the actual RGB texture to the emulated UYVY format. */ + + GLuint framebuffer; /* Framebuffer object name. */ + + GLuint vertexBuffer; /* Vertex attribute buffer. Position + texcoord. */ +} VMSVGA3DFORMATCONVERTER; + +/* Parameters for glVertexAttribPointer. */ +static const VertexAttribDesc aVertexAttribs[] = +{ + {2, GL_FLOAT, GL_FALSE, 16, 0 }, /* Position. */ + {2, GL_FLOAT, GL_FALSE, 16, 8 } /* Texcoord. */ +}; + +/* Triangle fan */ +static float const aAttribData[] = +{ + /* positions texcoords */ + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 1.0f +}; + +static const GLchar shaderHeaderSource[] = +{ + " #version 120\n" +}; + +static const GLchar vertexShaderSource[] = +{ + " attribute vec2 attrib0;\n" + " attribute vec2 attrib1;\n" + " void main(void)\n" + " {\n" + " gl_TexCoord[0].xy = attrib1;\n" + " gl_Position = vec4(attrib0.x, attrib0.y, 0.0f, 1.0f);\n" + " }\n" +}; + +static const GLchar fetchYUY2Source[] = +{ + " vec4 fetchYUV(vec4 texColor)\n" + " {\n" + " return vec4(texColor.b, texColor.g, texColor.r, texColor.a);\n" + " }\n" +}; + +static const GLchar fetchUYVYSource[] = +{ + " vec4 fetchYUV(vec4 texColor)\n" + " {\n" + " return vec4(texColor.g, texColor.b, texColor.a, texColor.r);\n" + " }\n" +}; + +static const GLchar YUV2RGBShaderSource[] = +{ + " uniform sampler2D sSourceTex;\n" + " uniform vec4 uTexInfo;\n" + " \n" + " const mat3 yuvCoeffs = mat3\n" + " (\n" + " 1.164383f, 0.0f, 1.596027f, // first column \n" + " 1.164383f, -0.391762f, -0.812968f, // second column\n" + " 1.164383f, 2.017232f, 0.0f // third column\n" + " );\n" + " \n" + " void main() {\n" + " // Input texcoords are in [0;1] range for the target.\n" + " vec2 texCoord = gl_TexCoord[0].xy;\n" + " // Convert to the target coords in pixels: xPixel = texCoord.x * TextureWidth. \n" + " float xTargetPixel = texCoord.x * uTexInfo.x;\n" + " // Source texture is half width, i.e. it contains data in pixels [0; width / 2 - 1].\n" + " float xSourcePixel = xTargetPixel / 2.0f;\n" + " // Remainder is about 0.25 for even pixels and about 0.75 for odd pixels.\n" + " float remainder = fract(xSourcePixel);\n" + " // Back to the normalized coords: texCoord.x = xPixel / Width.\n" + " texCoord.x = xSourcePixel * uTexInfo.z;\n" + " vec4 texColor = texture2D(sSourceTex, texCoord);\n" + " vec4 y0uy1v = fetchYUV(texColor);\n" + " // Get y0 for even x coordinates and y1 for odd ones.\n" + " float y = remainder < 0.5f ? y0uy1v.x : y0uy1v.z;\n" + " // Make a vector for easier calculation.\n" + " vec3 yuv = vec3(y, y0uy1v.y, y0uy1v.w);\n" + " yuv -= vec3(0.0627f, 0.502f, 0.502f);\n" + " vec3 bgr = yuv * yuvCoeffs;\n" + " //vec3 bgr;\n" + " //bgr.r = 1.164383 * yuv.x + 1.596027 * yuv.z;\n" + " //bgr.g = 1.164383 * yuv.x - 0.391762 * yuv.y - 0.812968 * yuv.z;\n" + " //bgr.b = 1.164383 * yuv.x + 2.017232 * yuv.y;\n" + " bgr = clamp(bgr, 0.0f, 1.0f);\n" + " gl_FragData[0] = vec4(bgr, 1.0f);\n" + " }\n" +}; + +static const GLchar storeYUY2Source[] = +{ + " vec4 storeYUV(float y0, float u, float y1, float v)\n" + " {\n" + " return vec4(y1, u, y0, v);\n" + " }\n" +}; + +static const GLchar storeUYVYSource[] = +{ + " vec4 storeYUV(float y0, float u, float y1, float v)\n" + " {\n" + " return vec4(u, y1, v, y0);\n" + " }\n" +}; + +static const GLchar RGB2YUVShaderSource[] = +{ + " uniform sampler2D sSourceTex;\n" + " uniform vec4 uTexInfo;\n" + " \n" + " const mat3 bgrCoeffs = mat3\n" + " (\n" + " 0.2578f, 0.5039f, 0.0977f, // first column \n" + " -0.1484f, -0.2891f, 0.4375f, // second column\n" + " 0.4375f, -0.3672f, -0.0703f // third column\n" + " );\n" + " const vec3 yuvShift = vec3(0.0647f, 0.5039f, 0.5039f);\n" + " \n" + " void main() {\n" + " // Input texcoords are in [0;1] range for the target.\n" + " vec2 texCoordDst = gl_TexCoord[0].xy;\n" + " // Convert to the target coords in pixels: xPixel = TexCoord.x * TextureWidth.\n" + " float xTargetPixel = texCoordDst.x * uTexInfo.x;\n" + " vec4 bgraOutputPixel;\n" + " if (xTargetPixel < uTexInfo.x / 2.0f)\n" + " {\n" + " // Target texture is half width, i.e. it contains data in pixels [0; width / 2 - 1].\n" + " // Compute the source texture coords for the pixels which will be used to compute the target pixel.\n" + " vec2 texCoordSrc = texCoordDst;\n" + " texCoordSrc.x *= 2.0f;\n" + " // Even pixel. Fetch two BGRA source pixels.\n" + " vec4 texColor0 = texture2D(sSourceTex, texCoordSrc);\n" + " // Advance one pixel (+ 1/Width)\n" + " texCoordSrc.x += uTexInfo.z;\n" + " vec4 texColor1 = texture2D(sSourceTex, texCoordSrc);\n" + " vec3 yuv0 = texColor0.rgb * bgrCoeffs;\n" + " yuv0 += yuvShift;\n" + " vec3 yuv1 = texColor1.rgb * bgrCoeffs;\n" + " yuv1 += yuvShift;\n" + " float y0 = yuv0.r;\n" + " float u = (yuv0.g + yuv1.g) / 2.0f;\n" + " float y1 = yuv1.r;\n" + " float v = (yuv0.b + yuv1.b) / 2.0f;\n" + " bgraOutputPixel = storeYUV(y0, u, y1, v);\n" + " }\n" + " else\n" + " {\n" + " // [width / 2; width - 1] pixels are not used. Set to something.\n" + " bgraOutputPixel = vec4(0.0f, 0.0f, 0.0f, 0.0f);\n" + " }\n" + " bgraOutputPixel = clamp(bgraOutputPixel, 0.0f, 1.0f);\n" + " gl_FragData[0] = bgraOutputPixel;\n" + " }\n" +}; + +#define GL_CHECK_ERROR() do { \ + VMSVGA3D_CHECK_LAST_ERROR_WARN(pState, pContext); \ + if (pContext->lastError != GL_NO_ERROR) \ + LogRelMax(10, ("VMSVGA: %s (%d): GL error 0x%x\n", __FUNCTION__, __LINE__, pContext->lastError)); \ +} while (0) + +/* Compile shaders and link a shader program. */ +static void createShaderProgram(PVMSVGA3DSTATE pState, + ShaderProgram *pProgram, + int cVertexSources, const GLchar **apszVertexSources, + int cFragmentSources, const GLchar **apszFragmentSources) +{ + AssertReturnVoid(pState->idActiveContext == VMSVGA3D_SHARED_CTX_ID); + + /* Everything is done on the shared context. The pState and pContext are for GL_CHECK_ERROR macro. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + + int success; + char szInfoLog[1024]; + + /* + * VERTEX shader. + */ + pProgram->vertexShader = pState->ext.glCreateShader(GL_VERTEX_SHADER); + GL_CHECK_ERROR(); + + pState->ext.glShaderSource(pProgram->vertexShader, cVertexSources, apszVertexSources, NULL); + GL_CHECK_ERROR(); + + pState->ext.glCompileShader(pProgram->vertexShader); + GL_CHECK_ERROR(); + + pState->ext.glGetShaderiv(pProgram->vertexShader, GL_COMPILE_STATUS, &success); + GL_CHECK_ERROR(); + if (!success) + { + pState->ext.glGetShaderInfoLog(pProgram->vertexShader, sizeof(szInfoLog), NULL, szInfoLog); + GL_CHECK_ERROR(); + LogRelMax(10, ("VMSVGA: Vertex shader compilation error:\n%s\n", szInfoLog)); + }; + + /* + * FRAGMENT shader. + */ + pProgram->fragmentShader = pState->ext.glCreateShader(GL_FRAGMENT_SHADER); + GL_CHECK_ERROR(); + + pState->ext.glShaderSource(pProgram->fragmentShader, cFragmentSources, apszFragmentSources, NULL); + GL_CHECK_ERROR(); + + pState->ext.glCompileShader(pProgram->fragmentShader); + GL_CHECK_ERROR(); + + pState->ext.glGetShaderiv(pProgram->fragmentShader, GL_COMPILE_STATUS, &success); + GL_CHECK_ERROR(); + if (!success) + { + pState->ext.glGetShaderInfoLog(pProgram->fragmentShader, sizeof(szInfoLog), NULL, szInfoLog); + GL_CHECK_ERROR(); + LogRelMax(10, ("VMSVGA: Fragment shader compilation error:\n%s\n", szInfoLog)); + }; + + /* + * Program + */ + pProgram->program = pState->ext.glCreateProgram(); + GL_CHECK_ERROR(); + + pState->ext.glAttachShader(pProgram->program, pProgram->vertexShader); + GL_CHECK_ERROR(); + + pState->ext.glAttachShader(pProgram->program, pProgram->fragmentShader); + GL_CHECK_ERROR(); + + pState->ext.glLinkProgram(pProgram->program); + GL_CHECK_ERROR(); + + pState->ext.glGetProgramiv(pProgram->program, GL_LINK_STATUS, &success); + if(!success) + { + pState->ext.glGetProgramInfoLog(pProgram->program, sizeof(szInfoLog), NULL, szInfoLog); + GL_CHECK_ERROR(); + LogRelMax(10, ("VMSVGA: Shader program link error:\n%s\n", szInfoLog)); + } + + pProgram->sSourceTex = pState->ext.glGetUniformLocation(pProgram->program, "sSourceTex"); + GL_CHECK_ERROR(); + + pProgram->uTexInfo = pState->ext.glGetUniformLocation(pProgram->program, "uTexInfo"); + GL_CHECK_ERROR(); +} + +/* Delete a shader program and associated shaders. */ +static void deleteShaderProgram(PVMSVGA3DSTATE pState, + ShaderProgram *pProgram) +{ + AssertReturnVoid(pState->idActiveContext == VMSVGA3D_SHARED_CTX_ID); + + /* Everything is done on the shared context. The pState and pContext are for GL_CHECK_ERROR macro. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + + if (pProgram->program) + { + if (pProgram->vertexShader) + { + pState->ext.glDetachShader(pProgram->program, pProgram->vertexShader); + GL_CHECK_ERROR(); + + pState->ext.glDeleteShader(pProgram->vertexShader); + GL_CHECK_ERROR(); + } + + if (pProgram->fragmentShader) + { + pState->ext.glDetachShader(pProgram->program, pProgram->fragmentShader); + GL_CHECK_ERROR(); + + pState->ext.glDeleteShader(pProgram->fragmentShader); + GL_CHECK_ERROR(); + } + + pState->ext.glDeleteProgram(pProgram->program); + GL_CHECK_ERROR(); + } + + RT_ZERO(*pProgram); +} + +/* Initialize the format conversion. Allocate and create necessary resources. */ +static void formatConversionInit(PVMSVGA3DSTATE pState) +{ + AssertReturnVoid(pState->idActiveContext == VMSVGA3D_SHARED_CTX_ID); + + PVMSVGA3DFORMATCONVERTER pConv = pState->pConv; + AssertReturnVoid(pConv); + + /* The pState and pContext variables are for GL_CHECK_ERROR macro. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + + pConv->pState = pState; + + /* + * Shader programs. + */ + static const GLchar *apszVertexShaderSources[] = + { + shaderHeaderSource, + vertexShaderSource + }; + + static const GLchar * apszYUY2ToRGBSources[] = + { + shaderHeaderSource, + fetchYUY2Source, + YUV2RGBShaderSource + }; + + static const GLchar *apszUYVYToRGBSources[] = + { + shaderHeaderSource, + fetchUYVYSource, + YUV2RGBShaderSource + }; + + static const GLchar *apszYUY2FromRGBSources[] = + { + shaderHeaderSource, + storeYUY2Source, + RGB2YUVShaderSource + }; + + static const GLchar *apszUYVYFromRGBSources[] = + { + shaderHeaderSource, + storeUYVYSource, + RGB2YUVShaderSource + }; + + createShaderProgram(pState, &pConv->programYUY2ToRGB, + RT_ELEMENTS(apszVertexShaderSources), apszVertexShaderSources, + RT_ELEMENTS(apszYUY2ToRGBSources), apszYUY2ToRGBSources); + + createShaderProgram(pState, &pConv->programUYVYToRGB, + RT_ELEMENTS(apszVertexShaderSources), apszVertexShaderSources, + RT_ELEMENTS(apszUYVYToRGBSources), apszUYVYToRGBSources); + + createShaderProgram(pState, &pConv->programYUY2FromRGB, + RT_ELEMENTS(apszVertexShaderSources), apszVertexShaderSources, + RT_ELEMENTS(apszYUY2FromRGBSources), apszYUY2FromRGBSources); + + createShaderProgram(pState, &pConv->programUYVYFromRGB, + RT_ELEMENTS(apszVertexShaderSources), apszVertexShaderSources, + RT_ELEMENTS(apszUYVYFromRGBSources), apszUYVYFromRGBSources); + + /* + * Create a framebuffer object which is used for rendering to a texture. + */ + pState->ext.glGenFramebuffers(1, &pConv->framebuffer); + GL_CHECK_ERROR(); + + pState->ext.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pConv->framebuffer); + GL_CHECK_ERROR(); + + static GLenum aDrawBuffers[] = { GL_COLOR_ATTACHMENT0 }; + pState->ext.glDrawBuffers(RT_ELEMENTS(aDrawBuffers), aDrawBuffers); + GL_CHECK_ERROR(); + + pState->ext.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + GL_CHECK_ERROR(); + + /* + * Vertex attribute array. + */ + pState->ext.glGenBuffers(1, &pConv->vertexBuffer); + GL_CHECK_ERROR(); + + pState->ext.glBindBuffer(GL_ARRAY_BUFFER, pConv->vertexBuffer); + GL_CHECK_ERROR(); + + pState->ext.glBufferData(GL_ARRAY_BUFFER, sizeof(aAttribData), aAttribData, GL_STATIC_DRAW); + GL_CHECK_ERROR(); + + pState->ext.glBindBuffer(GL_ARRAY_BUFFER, 0); + GL_CHECK_ERROR(); +} + +/* Delete everything. */ +static void formatConversionDestroy(PVMSVGA3DSTATE pState) +{ + AssertReturnVoid(pState->idActiveContext == VMSVGA3D_SHARED_CTX_ID); + + PVMSVGA3DFORMATCONVERTER pConv = pState->pConv; + AssertReturnVoid(pConv); + + /* The pState and pContext variables are for GL_CHECK_ERROR macro. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + + if (pConv->framebuffer != 0) + { + /* The code keeps nothing attached. */ + pState->ext.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pConv->framebuffer); + GL_CHECK_ERROR(); + + GLint texture = -1; + pState->ext.glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &texture); + GL_CHECK_ERROR(); + AssertMsg(texture == 0, ("texture %d\n", texture)); + + pState->ext.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + GL_CHECK_ERROR(); + + pState->ext.glDeleteFramebuffers(1, &pConv->framebuffer); + GL_CHECK_ERROR(); + + pConv->framebuffer = 0; + } + + deleteShaderProgram(pState, &pConv->programUYVYFromRGB); + deleteShaderProgram(pState, &pConv->programYUY2FromRGB); + deleteShaderProgram(pState, &pConv->programUYVYToRGB); + deleteShaderProgram(pState, &pConv->programYUY2ToRGB); + + if (pConv->vertexBuffer) + { + pState->ext.glDeleteBuffers(1, &pConv->vertexBuffer); + GL_CHECK_ERROR(); + + pConv->vertexBuffer = 0; + } + + pConv->pState = 0; +} + +/* Make use of a shader program for the current context and initialize the program uniforms. */ +static void setShaderProgram(PVMSVGA3DSTATE pState, + ShaderProgram *pProgram, + uint32_t cWidth, + uint32_t cHeight) +{ + AssertReturnVoid(pState->idActiveContext == VMSVGA3D_SHARED_CTX_ID); + + /* Everything is done on the shared context. The pState and pContext are for GL_CHECK_ERROR macro. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + + pState->ext.glUseProgram(pProgram->program); + GL_CHECK_ERROR(); + + pState->ext.glUniform1i(pProgram->sSourceTex, 0); + GL_CHECK_ERROR(); + + 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. */ + + pState->ext.glUniform4fv(pProgram->uTexInfo, 1, aTextureInfo); + GL_CHECK_ERROR(); +} + +/* Attach the texture which must be used as the render target + * to the GL_DRAW_FRAMEBUFFER as GL_COLOR_ATTACHMENT0. + */ +static void setRenderTarget(PVMSVGA3DSTATE pState, + GLuint texture, + uint32_t iMipmap) +{ + AssertReturnVoid(pState->idActiveContext == VMSVGA3D_SHARED_CTX_ID); + + PVMSVGA3DFORMATCONVERTER pConv = pState->pConv; + AssertReturnVoid(pConv); + + /* Everything is done on the shared context. The pState and pContext are for GL_CHECK_ERROR macro. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + + pState->ext.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pConv->framebuffer); + GL_CHECK_ERROR(); + + glBindTexture(GL_TEXTURE_2D, texture); + GL_CHECK_ERROR(); + + pState->ext.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, iMipmap); + GL_CHECK_ERROR(); + + glBindTexture(GL_TEXTURE_2D, 0); + GL_CHECK_ERROR(); + + Assert(pState->ext.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); +} + +/* Undo what setRenderTarget did. */ +static void unsetRenderTarget(PVMSVGA3DSTATE pState, + GLuint texture) +{ + AssertReturnVoid(pState->idActiveContext == VMSVGA3D_SHARED_CTX_ID); + + /* Everything is done on the shared context. The pState and pContext are for GL_CHECK_ERROR macro. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + + RT_NOREF(texture); + + pState->ext.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + GL_CHECK_ERROR(); + + pState->ext.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + GL_CHECK_ERROR(); +} + +/** Convert one texture to another. + * + * @param pState The backend. + * @param pCurrentContext The current context, which must be restored before returning. + * @param pSurface The surface which needs conversion. + * @param iMipmap The mipmap level which needs to be converted. + * @param fToRGB True for conversion from the intermediate texture emulated format + * to the RGB format of the actual texture. + * False for conversion from the actual RGB texture to the intermediate texture. + */ +static void doRender(PVMSVGA3DSTATE pState, + PVMSVGA3DCONTEXT pCurrentContext, + PVMSVGA3DSURFACE pSurface, + uint32_t iMipmap, + bool fToRGB) +{ + if (!fToRGB) + { + /** @todo Disable readback transfers for now. They cause crash in glDrawArrays with Mesa 19.2 after + * a previously converted texture is deleted and another texture is being converted. + * Such transfer are useless anyway for the emulated YUV formats and the guest should not need them usually. + */ + return; + } + + LogFunc(("formatConversion: idActiveContext %u, pConv %p, sid=%u, oglid=%u, oglidEmul=%u, mm=%u, %s\n", + pState->idActiveContext, pState->pConv, pSurface->id, pSurface->oglId.texture, pSurface->idEmulated, iMipmap, fToRGB ? "ToRGB" : "FromRGB")); + + PVMSVGA3DFORMATCONVERTER pConv = pState->pConv; + AssertReturnVoid(pConv); + + ShaderProgram *pProgram = NULL; + GLuint sourceTexture = 0; + GLuint targetTexture = 0; + if (fToRGB) + { + if (pSurface->format == SVGA3D_YUY2) + { + pProgram = &pConv->programYUY2ToRGB; + } + else if (pSurface->format == SVGA3D_UYVY) + { + pProgram = &pConv->programUYVYToRGB; + } + sourceTexture = pSurface->idEmulated; + targetTexture = pSurface->oglId.texture; + } + else + { + if (pSurface->format == SVGA3D_YUY2) + { + pProgram = &pConv->programYUY2FromRGB; + } + else if (pSurface->format == SVGA3D_UYVY) + { + pProgram = &pConv->programUYVYFromRGB; + } + sourceTexture = pSurface->oglId.texture; + targetTexture = pSurface->idEmulated; + } + + AssertReturnVoid(pProgram); + + PVMSVGA3DMIPMAPLEVEL pMipmapLevel; + int rc = vmsvga3dMipmapLevel(pSurface, 0, iMipmap, &pMipmapLevel); + AssertRCReturnVoid(rc); + + uint32_t const cWidth = pMipmapLevel->mipmapSize.width; + uint32_t const cHeight = pMipmapLevel->mipmapSize.height; + + /* Use the shared context, where all textures are created. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + VMSVGA3D_SET_CURRENT_CONTEXT(pState, pContext); + + setShaderProgram(pState, pProgram, cWidth, cHeight); + + setRenderTarget(pState, targetTexture, iMipmap); + + glViewport(0, 0, cWidth, cHeight); + GL_CHECK_ERROR(); + + glDisable(GL_DEPTH_TEST); + GL_CHECK_ERROR(); + + pState->ext.glActiveTexture(GL_TEXTURE0); + GL_CHECK_ERROR(); + + glBindTexture(GL_TEXTURE_2D, sourceTexture); + GL_CHECK_ERROR(); + + /* Make sure to set the simplest filter. Otherwise the conversion will not work. */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + GL_CHECK_ERROR(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + GL_CHECK_ERROR(); + + pState->ext.glBindBuffer(GL_ARRAY_BUFFER, pConv->vertexBuffer); + GL_CHECK_ERROR(); + + GLuint index; + for (index = 0; index < RT_ELEMENTS(aVertexAttribs); ++index) + { + pState->ext.glEnableVertexAttribArray(index); + GL_CHECK_ERROR(); + + pState->ext.glVertexAttribPointer(index, aVertexAttribs[index].size, aVertexAttribs[index].type, + aVertexAttribs[index].normalized, aVertexAttribs[index].stride, + (const GLvoid *)(uintptr_t)aVertexAttribs[index].offset); + GL_CHECK_ERROR(); + } + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + GL_CHECK_ERROR(); + + pState->ext.glBindBuffer(GL_ARRAY_BUFFER, 0); + GL_CHECK_ERROR(); + + glBindTexture(GL_TEXTURE_2D, 0); + GL_CHECK_ERROR(); + + unsetRenderTarget(pState, targetTexture); + + pState->ext.glUseProgram(0); + GL_CHECK_ERROR(); + + for (index = 0; index < RT_ELEMENTS(aVertexAttribs); ++index) + { + pState->ext.glDisableVertexAttribArray(index); + GL_CHECK_ERROR(); + } + + /* Restore the current context. */ + VMSVGA3D_SET_CURRENT_CONTEXT(pState, pCurrentContext); +} + +void FormatConvUpdateTexture(PVMSVGA3DSTATE pState, + PVMSVGA3DCONTEXT pCurrentContext, + PVMSVGA3DSURFACE pSurface, + uint32_t iMipmap) +{ + doRender(pState, pCurrentContext, pSurface, iMipmap, true); +} + +void FormatConvReadTexture(PVMSVGA3DSTATE pState, + PVMSVGA3DCONTEXT pCurrentContext, + PVMSVGA3DSURFACE pSurface, + uint32_t iMipmap) +{ + doRender(pState, pCurrentContext, pSurface, iMipmap, false); +} + +void vmsvga3dOnSharedContextDefine(PVMSVGA3DSTATE pState) +{ + /* Use the shared context, where all textures are created. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + VMSVGA3D_SET_CURRENT_CONTEXT(pState, pContext); + + /* + * Format conversion. + */ + Assert(pState->pConv == NULL); + + pState->pConv = (VMSVGA3DFORMATCONVERTER *)RTMemAllocZ(sizeof(VMSVGA3DFORMATCONVERTER)); + AssertReturnVoid(pState->pConv); + + formatConversionInit(pState); +} + +void vmsvga3dOnSharedContextDestroy(PVMSVGA3DSTATE pState) +{ + /* Use the shared context, where all textures are created. */ + PVMSVGA3DCONTEXT pContext = &pState->SharedCtx; + VMSVGA3D_SET_CURRENT_CONTEXT(pState, pContext); + + if (pState->pConv) + { + formatConversionDestroy(pState); + + RTMemFree(pState->pConv); + pState->pConv = NULL; + } +} |