vmware-svga/lib/util/screendraw.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

583 lines
15 KiB
C

/**********************************************************
* Copyright 2008-2009 VMware, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
**********************************************************/
/*
* screendraw.h --
*
* ScreenDraw is a small utility library for drawing
* into SVGA Screen Objects. It supports fills and text.
*
* It uses no system memory framebuffer at all- instead,
* we have a small GMRFB which contains our font glyphs
* and a tile buffer for blits.
*
* For text rendering, we support the Metalkit Console API.
*/
#include "types.h"
#include "svga_reg.h"
#include "datafile.h"
#include "gmr.h"
#include "svga.h"
#include "screen.h"
#include "screendraw.h"
#include "console_vga.h"
DECLARE_DATAFILE(fontData, ______lib_util_bitstream_vera_15_font_z);
#define TILE_SIZE 64
#define TILE_BUFFER_PIXELS (TILE_SIZE * TILE_SIZE)
#define TILE_BUFFER_BYTES (TILE_BUFFER_PIXELS * sizeof(uint32))
#define MAX_FONT_SIZE 200000
#define BACKGROUND_COLOR 0x000000 // This should match our font's color scheme
#define MARGIN_SIZE 10 // Blank margin around the screen edge
/*
* Our fontData begins with an array of CharMetrics, and is followed
* by raw image data for each glyph.
*/
typedef struct {
uint8 width;
uint8 height;
uint8 reserved[2];
uint32 offset;
} CharMetrics;
struct {
/* Dynamic state */
SVGASignedPoint position;
uint32 screenId;
uint32 screenWidth;
uint32 screenHeight;
/* Allocated resources */
SVGAGuestPtr tilePtr;
uint32 *tileBuffer;
SVGAGuestPtr fontPtr;
CharMetrics *metrics;
/* Tile buffer synchronization */
uint32 tileFence;
struct {
enum {
TILE_OTHER,
TILE_FILL,
TILE_CHECKERBOARD,
} type;
uint32 color;
} tileUsage;
} gScreenDraw;
/*
* Console API
*/
static fastcall void ScreenDrawBeginPanic(void);
static fastcall void ScreenDrawClear(void);
static fastcall void ScreenDrawMoveTo(int x, int y);
static fastcall void ScreenDrawWriteChar(char c);
static fastcall void ScreenDrawFlush(void);
/*
*-----------------------------------------------------------------------------
*
* ScreenDraw_Init --
*
* Initializes the ScreenDraw module. Requires that the GMR and
* Heap modules are already initialized.
*
* This allocates memory, allocates a GMR, decompresses our font,
* and sets the current Metalkit console to our text renderer.
*
* Results:
* None.
*
* Side effects:
* Allocates memory from the Heap.
* Defines a private GMR (gmrId).
* Decompresses the font.
*
*-----------------------------------------------------------------------------
*/
void
ScreenDraw_Init(uint32 gmrId)
{
const uint32 gmrSize = TILE_BUFFER_BYTES + MAX_FONT_SIZE;
PPN gmrPages = GMR_DefineContiguous(gmrId, (gmrSize + PAGE_MASK) / PAGE_SIZE);
gScreenDraw.tilePtr.gmrId = gmrId;
gScreenDraw.tilePtr.offset = 0;
gScreenDraw.tileBuffer = (uint32*)PPN_POINTER(gmrPages);
gScreenDraw.fontPtr.gmrId = gmrId;
gScreenDraw.fontPtr.offset = TILE_BUFFER_BYTES;
gScreenDraw.metrics = (CharMetrics*) (TILE_BUFFER_BYTES +
(uint8*)PPN_POINTER(gmrPages));
DataFile_Decompress(fontData, (void*)gScreenDraw.metrics, MAX_FONT_SIZE);
gConsole.beginPanic = ScreenDrawBeginPanic;
gConsole.clear = ScreenDrawClear;
gConsole.moveTo = ScreenDrawMoveTo;
gConsole.writeChar = ScreenDrawWriteChar;
gConsole.flush = ScreenDrawFlush;
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDraw_SetScreen --
*
* Define the ID and size of the screen we're drawing to.
*
* The size affects the dimensions of the rectangle we blit
* when clearing the screen, and text will wrap at the right
* edge.
*
* Results:
* None.
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
void
ScreenDraw_SetScreen(uint32 screenId, int width, int height)
{
gScreenDraw.screenId = screenId;
gScreenDraw.screenWidth = width;
gScreenDraw.screenHeight = height;
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDrawTiledRectangle --
*
* Internal function which blits multiple copies of the tile buffer
* to the screen in order to fill a rectangle.
*
* Results:
* None.
*
* Side effects:
* Updates the tile buffer fence.
*
*-----------------------------------------------------------------------------
*/
static void
ScreenDrawTiledRectangle(int left,
int top,
int right,
int bottom)
{
static const SVGAGMRImageFormat format = {{{ 32, 24 }}};
Screen_DefineGMRFB(gScreenDraw.tilePtr, TILE_SIZE * sizeof(uint32), format);
static const SVGASignedPoint srcOrigin = { 0, 0 };
SVGASignedRect destRect;
for (destRect.top = top; destRect.top < bottom; destRect.top += TILE_SIZE) {
destRect.bottom = MIN(bottom, destRect.top + TILE_SIZE);
for (destRect.left = left; destRect.left < right; destRect.left += TILE_SIZE) {
destRect.right = MIN(right, destRect.left + TILE_SIZE);
/*
* If this is a fill, annotate each blit.
*/
if (gScreenDraw.tileUsage.type == TILE_FILL) {
SVGAColorBGRX color;
color.value = gScreenDraw.tileUsage.color;
Screen_AnnotateFill(color);
}
Screen_BlitFromGMRFB(&srcOrigin, &destRect, gScreenDraw.screenId);
}
}
gScreenDraw.tileFence = SVGA_InsertFence();
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDraw_Rectangle --
*
* Draw a solid-color rectangle to the screen. The color
* is specified in hex RGB (0xRRGGBB) format.
*
* This implementation is only moderately efficient, and is
* provided mostly for simplicity and low memory usage. We have a
* single tile-sized DMA buffer. That buffer is cleared with the
* fill color, then we splat it onto the screen as many times as it
* takes to cover the rectangle.
*
* There are several ways this could be improved:
*
* - For small fills, we don't need to memset the entire tile buffer.
*
* - We could use multiple DMA buffers, so we'd sync less often.
*
* - If we really cared about framebuffer-less fill performance,
* we'd have an accelerated command for this- but we actually
* don't care that much.
*
* - If we wanted to cheat a bit, we could use a GMRFB
* bytesPerLine of zero. That should work in theory, but I
* don't think the SVGA device can guarantee that it will
* always work.
*
* Results:
* None.
*
* Side effects:
* May modify the tile buffer. If the tile buffer is busy,
* we will sync the FIFO. Modifies the GMRFB state.
*
*-----------------------------------------------------------------------------
*/
void
ScreenDraw_Rectangle(int left,
int top,
int right,
int bottom,
uint32 color)
{
if (gScreenDraw.tileUsage.type != TILE_FILL || gScreenDraw.tileUsage.color != color) {
SVGA_SyncToFence(gScreenDraw.tileFence);
gScreenDraw.tileUsage.type = TILE_FILL;
gScreenDraw.tileUsage.color = color;
memset32(gScreenDraw.tileBuffer, color, TILE_BUFFER_PIXELS);
}
ScreenDrawTiledRectangle(left, top, right, bottom);
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDraw_Checkerboard --
*
* Draw a gray checkerboard pattern within the specified rectangle.
*
* This is very similar to the implementation of
* ScreenDraw_Rectangle(), except that we fill the tile buffer with
* a checkerboard pattern instead of a solid color.
*
* Results:
* None.
*
* Side effects:
* May modify the tile buffer. If the tile buffer is busy,
* we will sync the FIFO. Modifies the GMRFB state.
*
*-----------------------------------------------------------------------------
*/
void
ScreenDraw_Checkerboard(int left,
int top,
int right,
int bottom)
{
if (gScreenDraw.tileUsage.type != TILE_CHECKERBOARD) {
const uint32 checkerSize = 8;
const uint32 color1 = 0x666677;
const uint32 color2 = 0x9999aa;
SVGA_SyncToFence(gScreenDraw.tileFence);
gScreenDraw.tileUsage.type = TILE_CHECKERBOARD;
uint32 *pixel = gScreenDraw.tileBuffer;
uint32 x, y;
for (y = 0; y < TILE_SIZE; y++) {
for (x = 0; x < TILE_SIZE; x++) {
*(pixel++) = (x ^ y) & checkerSize ? color1 : color2;
}
}
}
ScreenDrawTiledRectangle(left, top, right, bottom);
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDraw_Border --
*
* Draw a border along the interior edge of a rectangle.
*
* If 'width' is 1, we fill the pixels along the 'left' and 'top'
* edge, and the pixels just left/above the right and bottom
* edges. This corresponds to the outermost pixels that would have
* been written by ScreenDraw_Rectangle().
*
* If 'width' is greater than 1, we will cover additional pixels
* within these border pixels.
*
* Results:
* None.
*
* Side effects:
* May modify the tile buffer. If the tile buffer is busy,
* we will sync the FIFO. Modifies the GMRFB state.
*
*-----------------------------------------------------------------------------
*/
void
ScreenDraw_Border(int left,
int top,
int right,
int bottom,
uint32 color,
uint32 width)
{
ScreenDraw_Rectangle(left, top, right, top + width, color);
ScreenDraw_Rectangle(left, top, left + width, bottom, color);
ScreenDraw_Rectangle(right - width, top, right, bottom, color);
ScreenDraw_Rectangle(left, bottom - width, right, bottom, color);
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDrawBeginPanic --
*
* Prepare for a Console_Panic(). This switches back to the VGA
* console, and asks it to prepare instead.
*
* Results:
* None.
*
* Side effects:
* Switches consoles.
*
*-----------------------------------------------------------------------------
*/
fastcall void
ScreenDrawBeginPanic(void)
{
ConsoleVGA_Init();
gConsole.beginPanic();
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDrawClear --
*
* Reset the cursor location, and clear the screen.
*
* Results:
* None.
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
fastcall void
ScreenDrawClear(void)
{
ScreenDrawMoveTo(MARGIN_SIZE, MARGIN_SIZE);
ScreenDraw_Rectangle(0, 0, gScreenDraw.screenWidth, gScreenDraw.screenHeight,
BACKGROUND_COLOR);
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDrawMoveTo --
*
* Set the cursor location, in pixels.
*
* Results:
* None.
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
fastcall void
ScreenDrawMoveTo(int x, int y)
{
gScreenDraw.position.x = x;
gScreenDraw.position.y = y;
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDrawWriteChar --
*
* Write one visible glyph to the screen, and adjust the glyph position.
*
* Results:
* None.
*
* Side effects:
* May redefine the current GMRFB.
*
*-----------------------------------------------------------------------------
*/
fastcall void
ScreenDrawWriteChar(char c)
{
CharMetrics *cm = &gScreenDraw.metrics[(uint8)c];
if (c == '\n') {
gScreenDraw.position.x = MARGIN_SIZE;
gScreenDraw.position.y += gScreenDraw.metrics[(uint8)' '].height;
return;
}
if (!cm->height) {
/* Character not present in this font */
return;
}
SVGAGuestPtr gPtr = gScreenDraw.fontPtr;
gPtr.offset += cm->offset;
static const SVGAGMRImageFormat format = {{{ 32, 24 }}};
Screen_DefineGMRFB(gPtr, sizeof(uint32) * cm->width, format);
SVGASignedPoint blitOrigin = { 0, 0 };
SVGASignedRect blitDest = {
gScreenDraw.position.x,
gScreenDraw.position.y,
gScreenDraw.position.x + cm->width,
gScreenDraw.position.y + cm->height,
};
Screen_BlitFromGMRFB(&blitOrigin, &blitDest, gScreenDraw.screenId);
gScreenDraw.position.x += cm->width;
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDraw_WrapText --
*
* This is a utility function for word-wrapping text at runtime. We wrap
* a provided string in-place, by replacing spaces with newlines.
*
* Results:
* None.
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
void
ScreenDraw_WrapText(char *text, // IN/OUT
int width) // IN
{
char *word = text;
int x = 0;
while (*word) {
if (*word == '\n') {
x = 0;
}
if (*word == ' ' && x > 0) {
/*
* We're pointing just prior to a word that we can wrap. Should we?
*/
char *p = word + 1;
int wordWidth = 0;
while (*p != ' ' && *p != '\0' && *p != '\n') {
wordWidth += gScreenDraw.metrics[(uint8) *p].width;
p++;
}
if (x + wordWidth > width) {
x = 0;
*word = '\n';
}
}
word++;
x += gScreenDraw.metrics[(uint8) *word].width;
}
}
/*
*-----------------------------------------------------------------------------
*
* ScreenDrawFlush --
*
* No-op. We don't need to flush after writing text.
*
* Results:
* None.
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
fastcall void
ScreenDrawFlush(void)
{
/* No-op */
}