vmware-svga/examples/screen-render-test/main.c
Micah Dowty 68478eab4b There are many new tests and examples that we wrote internally at VMware,
but couldn't release immediately since they depended on virtual GPU features
which were not yet publicly released in any products.  This checkin moves those
features from our internal repository to the open source repository. Future   
development on these tests and examples will take place directly in the open
source repository.

The primary feature added by this patch is 'Screen Object', a new dynamic
display management extension supported by Workstation 7.0 and Fusion 3.0.
See the README for a quick explanation.
2009-10-21 20:20:49 +00:00

632 lines
17 KiB
C

/*
* This is a test which demonstrates many features of the Screen
* Object rendering model:
*
* - GMR-to-screen blits from multiple GMRFBs onto multiple screens
* - SVGA3D-to-screen blits, with scaling, onto multiple screens
* - screen-to-GMR blits
* - Video Overlay with GMRs and Screen Objects
*
* For a complete test of Screen Object, several other tests are also
* necessary:
*
* - Per-screen cursors (screen-multimon test)
* - Screen creation, destruction, and redefinition (screen-multimon)
* - Checkpointing
*
* This example requires SVGA Screen Object and SVGA3D support.
*/
#include "svga.h"
#include "svga3d.h"
#include "svga3dutil.h"
#include "matrix.h"
#include "math.h"
#include "gmr.h"
#include "screen.h"
#include "intr.h"
#include "screendraw.h"
#include "datafile.h"
/*
* 3D Rendering Definitions
*/
typedef struct {
float position[3];
uint32 color;
} MyVertex;
uint32 vertexSid, indexSid;
const int surfWidth = 1024;
const int surfHeight = 512;
SVGA3dSurfaceImageId colorImage;
SVGA3dSurfaceImageId depthImage;
static const MyVertex vertexData[] = {
{ {-1, -1, -1}, 0xFFFFFFFF },
{ {-1, -1, 1}, 0xFFFFFF00 },
{ {-1, 1, -1}, 0xFFFF00FF },
{ {-1, 1, 1}, 0xFFFF0000 },
{ { 1, -1, -1}, 0xFF00FFFF },
{ { 1, -1, 1}, 0xFF00FF00 },
{ { 1, 1, -1}, 0xFF0000FF },
{ { 1, 1, 1}, 0xFF000000 },
};
#define QUAD(a,b,c,d) a, b, d, d, c, a
static const uint16 indexData[] = {
QUAD(0,1,2,3), // -X
QUAD(4,5,6,7), // +X
QUAD(0,1,4,5), // -Y
QUAD(2,3,6,7), // +Y
QUAD(0,2,4,6), // -Z
QUAD(1,3,5,7), // +Z
};
#undef QUAD
const uint32 numTriangles = arraysize(indexData) / 3;
/*
* initScreens --
*
* Set up our Screen Objects, and label them.
*/
void
initScreens(void)
{
int i;
/*
* Define two screens:
*
* +-------+
* |0 |
* | |
* +--+----+-+
* |1 |
* +------+
*
* The screen 0 is 799x405, the screen 1 is 600x200.
* Neither screen is rooted at 0,0.
*/
SVGAScreenObject screens[] = {
{
.structSize = sizeof(SVGAScreenObject),
.id = 0,
.flags = SVGA_SCREEN_HAS_ROOT | SVGA_SCREEN_IS_PRIMARY,
.size = { 799, 405 },
.root = { -1234, 5678 },
},
{
.structSize = sizeof(SVGAScreenObject),
.id = 1,
.flags = SVGA_SCREEN_HAS_ROOT,
.size = { 600, 200 },
.root = { -1234 + 302, 5678 + 405 },
},
};
for (i = 0; i < arraysize(screens); i++) {
const SVGAScreenObject *screen = &screens[i];
Screen_Define(screen);
ScreenDraw_SetScreen(screen->id, screen->size.width, screen->size.height);
Console_Clear();
Console_MoveTo(5, screen->size.height - 20);
Console_Format("Screen #%d\n", screen->id);
ScreenDraw_Border(0, 0, screen->size.width, screen->size.height, 0xFF0000, 1);
}
}
/*
* initOverlays --
*
* Set up a video overlay using a non-default screen ID and a
* system memory GMR. These are both new features included in
* the Screen Object extension.
*/
void
initOverlays(void)
{
/*
* A video test card, in UYVY format.
*
* It's a 720x576 pixel 4:3 aspect test card designed by Barney
* Wol. (http://www.barney-wol.net/testpatterns)
*
* Decompress it into a new system memory GMR.
*/
DECLARE_DATAFILE(testCardFile, wols4x3_yuv_z);
const uint32 gmrId = 1;
const int videoWidth = 720;
const int videoHeight = 576;
const int videoBytes = videoWidth * videoHeight * 2;
const int videoPages = (videoBytes + PAGE_MASK) / PAGE_SIZE;
PPN videoFirstPage = GMR_DefineContiguous(gmrId, videoPages);
DataFile_Decompress(testCardFile, PPN_POINTER(videoFirstPage), videoBytes);
/*
* Display the video using Screen 1's coordinate system, but
* actually have it overlap the right edge of the line segment that
* screens 0 and 1 share.
*/
SVGAOverlayUnit overlay = {
.enabled = TRUE,
.format = VMWARE_FOURCC_UYVY,
.flags = SVGA_VIDEO_FLAG_COLORKEY,
.colorKey = 0x000000,
.width = videoWidth,
.height = videoHeight,
.srcWidth = videoWidth,
.srcHeight = videoHeight,
.pitches[0] = videoWidth * 2,
.dataGMRId = gmrId,
.dataOffset = 0,
.dstX = 220,
.dstY = -100,
.dstWidth = 320,
.dstHeight = 240,
.dstScreenId = 1,
};
SVGA_VideoSetAllRegs(0, &overlay, SVGA_VIDEO_DST_SCREEN_ID);
/*
* XXX: Flush twice, to work around a bug in legacy backends.
* New SWB backends don't have this problem, but it's harmless.
*/
SVGA_VideoFlush(0);
SVGA_VideoFlush(0);
/*
* Draw a border around the video
*/
ScreenDraw_SetScreen(1, 0, 0);
ScreenDraw_Border(overlay.dstX - 1, overlay.dstY - 1,
overlay.dstX + overlay.dstWidth + 1,
overlay.dstY + overlay.dstHeight + 1,
0xFFFF00, 1);
/*
* Some text to explain the video
*/
Console_MoveTo(overlay.dstX, overlay.dstY + overlay.dstHeight + 5);
Console_Format("Video overlay on Screen 1, sysmem GMR");
}
/*
* setup3D --
*
* Allocate 3D resources that are used by draw3D() and cubeLoop().
*/
void
setup3D(void)
{
/*
* Set up some 3D resources: A color buffer, depth buffer, vertex
* buffer, and index buffer. Load the VB and IB with data for a
* cube with unique vertex colors.
*/
colorImage.sid = SVGA3DUtil_DefineSurface2D(surfWidth, surfHeight, SVGA3D_X8R8G8B8);
depthImage.sid = SVGA3DUtil_DefineSurface2D(surfWidth, surfHeight, SVGA3D_Z_D16);
SVGA3D_DefineContext(CID);
vertexSid = SVGA3DUtil_DefineStaticBuffer(vertexData, sizeof vertexData);
indexSid = SVGA3DUtil_DefineStaticBuffer(indexData, sizeof indexData);
}
/*
* drawCube --
*
* Draw a cube with the specified viewport and background color.
* Every time we draw a cube, it rotates a little.
*/
void
drawCube(SVGA3dRect *viewport, uint32 bgColor)
{
static float angle = 0.5f;
SVGA3dRect *rect;
Matrix perspectiveMat;
SVGA3dTextureState *ts;
SVGA3dRenderState *rs;
SVGA3D_SetRenderTarget(CID, SVGA3D_RT_COLOR0, &colorImage);
SVGA3D_SetRenderTarget(CID, SVGA3D_RT_DEPTH, &depthImage);
SVGA3D_SetViewport(CID, viewport);
SVGA3D_SetZRange(CID, 0.0f, 1.0f);
SVGA3D_BeginSetRenderState(CID, &rs, 5);
{
rs[0].state = SVGA3D_RS_BLENDENABLE;
rs[0].uintValue = FALSE;
rs[1].state = SVGA3D_RS_ZENABLE;
rs[1].uintValue = TRUE;
rs[2].state = SVGA3D_RS_ZWRITEENABLE;
rs[2].uintValue = TRUE;
rs[3].state = SVGA3D_RS_ZFUNC;
rs[3].uintValue = SVGA3D_CMP_LESS;
rs[4].state = SVGA3D_RS_LIGHTINGENABLE;
rs[4].uintValue = FALSE;
}
SVGA_FIFOCommitAll();
SVGA3D_BeginSetTextureState(CID, &ts, 4);
{
ts[0].stage = 0;
ts[0].name = SVGA3D_TS_BIND_TEXTURE;
ts[0].value = SVGA3D_INVALID_ID;
ts[1].stage = 0;
ts[1].name = SVGA3D_TS_COLOROP;
ts[1].value = SVGA3D_TC_SELECTARG1;
ts[2].stage = 0;
ts[2].name = SVGA3D_TS_COLORARG1;
ts[2].value = SVGA3D_TA_DIFFUSE;
ts[3].stage = 0;
ts[3].name = SVGA3D_TS_ALPHAARG1;
ts[3].value = SVGA3D_TA_DIFFUSE;
}
SVGA_FIFOCommitAll();
/*
* Clear unused areas of the surface to white
*/
SVGA3D_BeginClear(CID, SVGA3D_CLEAR_COLOR | SVGA3D_CLEAR_DEPTH,
0xFFFFFFFF, 1.0f, 0, &rect, 1);
rect->x = 0;
rect->y = 0;
rect->w = surfWidth;
rect->w = surfHeight;
SVGA_FIFOCommitAll();
/*
* Draw the background color
*/
SVGA3D_BeginClear(CID, SVGA3D_CLEAR_COLOR | SVGA3D_CLEAR_DEPTH,
bgColor | 0x42000000, 1.0f, 0, &rect, 1);
*rect = *viewport;
SVGA_FIFOCommitAll();
SVGA3dVertexDecl *decls;
SVGA3dPrimitiveRange *ranges;
Matrix view;
Matrix_Copy(view, gIdentityMatrix);
Matrix_Scale(view, 0.5, 0.5, 0.5, 1.0);
Matrix_RotateX(view, 30.0 * M_PI / 180.0);
Matrix_RotateY(view, angle);
Matrix_Translate(view, 0, 0, 2.5);
angle += 0.01;
Matrix_Perspective(perspectiveMat, 45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
SVGA3D_SetTransform(CID, SVGA3D_TRANSFORM_WORLD, gIdentityMatrix);
SVGA3D_SetTransform(CID, SVGA3D_TRANSFORM_PROJECTION, perspectiveMat);
SVGA3D_SetTransform(CID, SVGA3D_TRANSFORM_VIEW, view);
SVGA3D_BeginDrawPrimitives(CID, &decls, 2, &ranges, 1);
{
decls[0].identity.type = SVGA3D_DECLTYPE_FLOAT3;
decls[0].identity.usage = SVGA3D_DECLUSAGE_POSITION;
decls[0].array.surfaceId = vertexSid;
decls[0].array.stride = sizeof(MyVertex);
decls[0].array.offset = offsetof(MyVertex, position);
decls[1].identity.type = SVGA3D_DECLTYPE_D3DCOLOR;
decls[1].identity.usage = SVGA3D_DECLUSAGE_COLOR;
decls[1].array.surfaceId = vertexSid;
decls[1].array.stride = sizeof(MyVertex);
decls[1].array.offset = offsetof(MyVertex, color);
ranges[0].primType = SVGA3D_PRIMITIVE_TRIANGLELIST;
ranges[0].primitiveCount = numTriangles;
ranges[0].indexArray.surfaceId = indexSid;
ranges[0].indexArray.stride = sizeof(uint16);
ranges[0].indexWidth = sizeof(uint16);
}
SVGA_FIFOCommitAll();
}
/*
* draw3D --
*
* Set up some 3D resources, and draw a simple cube scene to
* several places on the frontbuffer. Each cube is drawn using a
* distinct background color.
*/
void
draw3D(void)
{
/*
* Scenes to render
*/
const int dstWidth = 160;
const int dstHeight = 120;
struct {
uint32 screenId;
SVGA3dRect viewport;
int x, y;
uint32 bgColor;
const char *label;
} scenes[] = {
/*
* These scenes all use different but overlapping parts of the
* render target, so it's quite obvious if the wrong image is
* being displayed- either you'll see part of the frame
* overwritten by a different frame, or the image will be
* misaligned.
*/
{ 0, { 123, 65, 160, 120 }, 320, 20 + 145 * 0, 0x800000, "Red" }, // Non-scaled
{ 0, { 150, 82, 320, 240 }, 320, 20 + 145 * 1, 0x008000, "Green" }, // Scaled
{ 0, { 85, 32, 400, 300 }, 320, 20 + 145 * 2, 0x000080, "Blue" }, // Scaled
{ 0, { 160, 40, 160, 120 }, 320, 20 + 145 * 3, 0xae3aff, "Purple" }, // Non-scaled
};
int i;
for (i = 0; i < arraysize(scenes); i++) {
drawCube(&scenes[i].viewport, scenes[i].bgColor);
SVGASignedRect srcRect = { scenes[i].viewport.x,
scenes[i].viewport.y,
scenes[i].viewport.x + scenes[i].viewport.w,
scenes[i].viewport.y + scenes[i].viewport.h };
SVGASignedRect dstRect = { scenes[i].x,
scenes[i].y,
scenes[i].x + dstWidth,
scenes[i].y + dstHeight };
/*
* Draw some text underneath the Present. If you can see this
* text, something is wrong.
*/
ScreenDraw_SetScreen(scenes[i].screenId, 0, 0);
Console_MoveTo(dstRect.left + 10, (dstRect.top + dstRect.bottom) / 2);
Console_WriteString("XXX: 2D!");
/*
* Present our scene to the front buffer using the new surface-to-screen blit.
*/
SVGA3D_BlitSurfaceToScreen(&colorImage, &srcRect,
scenes[i].screenId, &dstRect);
/*
* Draw on the frontbuffer to label this scene, and draw
* a border around it.
*/
ScreenDraw_SetScreen(scenes[i].screenId, 0, 0);
Console_MoveTo(dstRect.left, dstRect.top);
Console_WriteString(scenes[i].label);
ScreenDraw_Border(dstRect.left - 1, dstRect.top - 1,
dstRect.right + 1, dstRect.bottom + 1,
0xFFFF00, 1);
}
}
/*
* cubeLoop --
*
* Draw a spinning cube in an infinite loop, using the same
* backbuffer and other resources that we used on the earlier
* cubes.
*/
void
cubeLoop(void)
{
/* Draw a label */
ScreenDraw_SetScreen(1, 0, 0);
Console_MoveTo(230, -265);
Console_WriteString("Spinning, Orange:");
while (1) {
SVGA3dRect viewport = { 0, 0, 160, 120 };
SVGASignedRect srcRect = { 0, 0, 160, 120 };
SVGASignedRect dstRect = { 230, -245, 230+160, -245+120 };
drawCube(&viewport, 0xd9a54a);
SVGA3D_BlitSurfaceToScreen(&colorImage, &srcRect, 1, &dstRect);
}
}
/*
* complementBytes --
*
* For a range of bytes, do an in-place complement. Every bit is inverted.
*/
void
complementBytes(uint8 *ptr, uint32 count)
{
while (count) {
*ptr ^= 0xFF;
ptr++;
count--;
}
}
/*
* doBlits --
*
* Use screen-to-GMR and GMR-to-screen blits in order to copy from
* one part of the screen to another. To prove that the guest can
* access the GMR data, we invert part of the image.
*/
void
doBlits()
{
struct {
SVGAGMRImageFormat format;
int screenId;
int srcx, srcy;
int dstx, dsty;
int labelx, labely;
const char *label;
} blits[] = {
/*
* Copy the red image, using 24-bit color, and label the bottom
* half (inverse) as Cyan.
*/
{
{{{32, 24}}}, 0,
319, 19, // src
134, 19, // dest
1, 103, // label
"Cyan",
},
/*
* Copy the green image using 15-bit color, and label the bottom as pink.
*/
{
{{{16, 15}}}, 0,
319, 164, // src
134, 164, // dest
1, 103, // label
"Pink",
},
/*
* Try to copy the blue image. The screen-to-GMR blit will fail, since
* we don't support screen-to-GMR blits that aren't contained within one
* screen. So we'll end up drawing gray (the color we clear the buffer with)
* and a ligher gray on the bottom where we invert.
*/
{
{{{32, 24}}}, 0,
319, 309, // src
134, 309, // dest
1, 1, // label
"Gray, no cube",
},
/*
* Now try to copy part of the video overlay from Screen 2.
* We should get the text and border, but no video.
*/
{
{{{16, 16}}}, 1,
200, 70, // src
230, -400, // dest
0, 0, // label
"Frame & Text, No Video",
},
};
const int width = 162;
const int height = 122;
const SVGASignedPoint gmrOrigin = { 123, 4 };
const int maxBytesPerLine = (width + gmrOrigin.x) * 4;
const int numBytes = maxBytesPerLine * (height + gmrOrigin.y);
const int offset = 12345;
const int numPages = (offset + numBytes + PAGE_MASK) / PAGE_SIZE;
const int gmrId = 2;
PPN firstPage = GMR_DefineContiguous(gmrId, numPages);
int i;
for (i = 0; i < arraysize(blits); i++) {
/* Wait for any previous DMA to finish */
SVGA_SyncToFence(SVGA_InsertFence());
/* Clear the buffer to 0x42 (will appear dark grey) */
memset(PPN_POINTER(firstPage), 0x42, numPages * PAGE_SIZE);
SVGAGuestPtr gPtr = { gmrId, offset };
uint32 bytesPerLine = blits[i].format.bitsPerPixel * width / 8;
Screen_DefineGMRFB(gPtr, bytesPerLine, blits[i].format);
SVGASignedPoint srcPoint = { blits[i].srcx, blits[i].srcy };
SVGASignedPoint dstPoint = { blits[i].dstx, blits[i].dsty };
SVGASignedRect srcRect = { srcPoint.x, srcPoint.y,
srcPoint.x + width, srcPoint.y + height };
SVGASignedRect dstRect = { dstPoint.x, dstPoint.y,
dstPoint.x + width, dstPoint.y + height };
Screen_BlitToGMRFB(&gmrOrigin, &srcRect, blits[i].screenId);
/* Wait for the DMA to finish */
SVGA_SyncToFence(SVGA_InsertFence());
/* Invert the colors in the bottom half of the image */
uint8 *pixels = (offset +
bytesPerLine * (gmrOrigin.y + height/2) +
blits[i].format.bitsPerPixel * gmrOrigin.x / 8 +
(uint8*)PPN_POINTER(firstPage));
complementBytes(pixels, bytesPerLine * height/2);
/* Copy it back */
Screen_BlitFromGMRFB(&gmrOrigin, &dstRect, blits[i].screenId);
/* Draw a label for the bottom half */
ScreenDraw_SetScreen(blits[i].screenId, 0, 0);
Console_MoveTo(dstRect.left + blits[i].labelx, dstRect.top + blits[i].labely);
Console_WriteString(blits[i].label);
}
}
/*
* main --
*
* Initialization, main loop.
*/
int
main(void)
{
Intr_Init();
Intr_SetFaultHandlers(SVGA_DefaultFaultHandler);
SVGA_Init();
GMR_Init();
Heap_Reset();
SVGA_SetMode(0, 0, 32);
SVGA3D_Init();
Screen_Init();
ScreenDraw_Init(0);
initScreens();
setup3D();
draw3D();
initOverlays();
doBlits();
cubeLoop();
return 0;
}