/* * 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; }