vmware-svga/lib/util/svga3dutil.c
2009-04-13 07:05:42 +00:00

750 lines
20 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.
*
**********************************************************/
/*
* svga3dutil.c --
*
* Higher-level convenience functions built on top of the SVGA3D
* FIFO command layer.
*/
#include "svga3dutil.h"
#include "intr.h"
FullscreenState gFullscreen;
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_InitFullscreen --
*
* This is a "big hammer" for initializing the SVGA device in
* our simple 3D example programs. It does the following:
*
* - Initializes interrupts
* - Initializes the SVGA adapter
* - Switches video modes
* - Initializes the absolute mouse
* - Initializes the 3D subsystem
* - Creates a color buffer and depth buffer
* - Creates a context
* - Attaches the color/depth buffers to the context
* - Sets the viewport
* - Sets default render states
*
* Results:
* void.
*
* Side effects:
* Panic if anything fails.
* Stores the fullscreen color buffer surface ID.
*
*----------------------------------------------------------------------
*/
void
SVGA3DUtil_InitFullscreen(uint32 cid, // IN
uint32 width, // IN
uint32 height) // IN
{
SVGA3dRenderState *rs;
gFullscreen.screen.x = 0;
gFullscreen.screen.y = 0;
gFullscreen.screen.w = width;
gFullscreen.screen.h = height;
Intr_Init();
Intr_SetFaultHandlers(SVGA_DefaultFaultHandler);
SVGA_Init();
SVGA_SetMode(width, height, 32);
VMBackdoor_MouseInit(TRUE);
SVGA3D_Init();
gFullscreen.colorImage.sid = SVGA3DUtil_DefineSurface2D(width, height,
SVGA3D_X8R8G8B8);
gFullscreen.depthImage.sid = SVGA3DUtil_DefineSurface2D(width, height,
SVGA3D_Z_D16);
SVGA3D_DefineContext(cid);
SVGA3D_SetRenderTarget(cid, SVGA3D_RT_COLOR0, &gFullscreen.colorImage);
SVGA3D_SetRenderTarget(cid, SVGA3D_RT_DEPTH, &gFullscreen.depthImage);
SVGA3D_SetViewport(cid, &gFullscreen.screen);
SVGA3D_SetZRange(cid, 0.0f, 1.0f);
/*
* The device defaults to flat shading, but to retain compatibility
* across OpenGL and Direct3D it may be much slower in this
* mode. Usually we don't want flat shading, so go ahead and switch
* into smooth shading mode.
*
* Note that this is a per-context render state.
*
* XXX: There is also a bug in VMware Workstation 6.5.2 which shows
* up if you're in flat shading mode and you're using a drawing
* command which does not include an SVGA3dVertexDivisor array.
* Avoiding flat shading is one workaround, another is to include
* a dummy SVGA3dVertexDivisor array on every draw.
*/
SVGA3D_BeginSetRenderState(cid, &rs, 1);
{
rs[0].state = SVGA3D_RS_SHADEMODE;
rs[0].uintValue = SVGA3D_SHADEMODE_SMOOTH;
}
SVGA_FIFOCommitAll();
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_PresentFullscreen --
*
* This is a simplified version of SVGA3D_BeginPresent(), which
* assumes that we're running in full-screen mode and that the
* whole screen needs updating.
*
* It also includes the recommended flow control, to prevent
* the SVGA3D device from lagging too far behind the driver-
* we use FIFO fences to ensure that only one Present command
* is in the FIFO at once.
*
* This is much better than performing a full Sync after each
* Present, since it allows the guest to be preparing frame N+1
* while the host is still rendering frame N.
*
* Note that this simplified logic needs to be modified if the
* application/driver is presenting from multiple
* surfaces. Ideally, we actaully want only a single frame per
* surface in the FIFO at once. It is recommended that drivers
* store lastPresentFence per-surface.
*
* Results:
* None.
*
* Side effects:
* SVGA_SyncToFence().
*
*----------------------------------------------------------------------
*/
void
SVGA3DUtil_PresentFullscreen(void)
{
SVGA3dCopyRect *cr;
SVGA_SyncToFence(gFullscreen.lastPresentFence);
SVGA3D_BeginPresent(gFullscreen.colorImage.sid, &cr, 1);
memset(cr, 0, sizeof *cr);
cr->w = gSVGA.width;
cr->h = gSVGA.height;
SVGA_FIFOCommitAll();
gFullscreen.lastPresentFence = SVGA_InsertFence();
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_AsyncCall --
*
* This is a simple asynchronous call mechanism, which is used to
* invoke a specified function once the SVGA3D device's FIFO
* processing has reached the current point in the command
* stream. It can be used, for example, to asynchronously garbage
* collect DMA buffers or asynchronously handle downloads from
* host VRAM.
*
* This single function both dispatches previous calls and
* optionall enqueues a new call.
*
* A fixed number of asynchronous calls may be in flight at any
* given time. If MAX_ASYNC_CALLS calls are pending, we wait for
* the oldest one to finish.
*
* You can call this function with handler==NULL to just flush
* any existing async calls which have completed.
*
* Results:
* None.
*
* Side effects:
* SVGA_SyncToFence(), any side-effects caused by other async
* call handlers. If we're enqueueing a handler, this inserts
* a FIFO fence.
*
*----------------------------------------------------------------------
*/
void
SVGA3DUtil_AsyncCall(AsyncCallFn handler, // IN (optional)
void *arg) // IN (optional)
{
static struct {
uint32 head, tail, count;
struct {
AsyncCallFn handler;
void *arg;
uint32 fence;
} calls[MAX_ASYNC_CALLS];
} queue;
if (queue.count > MAX_ASYNC_CALLS ||
queue.head >= MAX_ASYNC_CALLS ||
queue.tail >= MAX_ASYNC_CALLS) {
SVGA_Panic("Async call queue corrupted");
}
if (queue.count == MAX_ASYNC_CALLS) {
SVGA_SyncToFence(queue.calls[queue.tail].fence);
}
while (queue.count && SVGA_HasFencePassed(queue.calls[queue.tail].fence)) {
queue.calls[queue.tail].handler(queue.calls[queue.tail].arg);
queue.tail = (queue.tail + 1) % MAX_ASYNC_CALLS;
queue.count--;
}
if (handler) {
queue.calls[queue.head].handler = handler;
queue.calls[queue.head].arg = arg;
queue.calls[queue.head].fence = SVGA_InsertFence();
queue.head = (queue.head + 1) % MAX_ASYNC_CALLS;
queue.count++;
}
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_UpdateFPSCounter --
*
* This is a simple self-contained frame and frame rate counter.
*
* Initialize an FPSCounterState structure to zero (or allocate
* it in the BSS segment) and call this function once per frame.
* Any time it returns TRUE, the caller should update the
* on-screen display with a new frame rate.
*
* Guaranteed to return TRUE on the first call.
*
* Results:
* TRUE if the frame rate needs updating.
*
* Side effects:
* Occasionally requests the current host wallclock time via the
* VMware backdoor port.
*
*----------------------------------------------------------------------
*/
static char
FPSDigit(uint32 n, uint32 divisor)
{
n /= divisor;
if (n == 0) {
return ' ';
} else {
return (n % 10) + '0';
}
}
Bool
SVGA3DUtil_UpdateFPSCounter(FPSCounterState *self) // IN/OUT
{
uint32 msecDiff;
uint32 frameDiff;
if (!self->initialized) {
VMBackdoor_GetTime(&self->now);
self->lastUpdate.time = self->now;
self->initialized = TRUE;
return TRUE;
}
self->frame++;
/*
* Only check the time every several frames, to avoid the small
* overhead of making backdoor calls on every frame.
*/
if (self->frame & 0x0F) {
return FALSE;
}
VMBackdoor_GetTime(&self->now);
msecDiff = VMBackdoor_TimeDiffUS(&self->lastUpdate.time, &self->now) / 1000;
if (msecDiff < 500) {
/* Too soon since the last update */
return FALSE;
}
frameDiff = self->frame - self->lastUpdate.frame;
self->hundredths = (frameDiff * (1000 * 100)) / msecDiff;
self->text[0] = FPSDigit(self->hundredths, 1000000);
self->text[1] = FPSDigit(self->hundredths, 100000);
self->text[2] = FPSDigit(self->hundredths, 10000);
self->text[3] = FPSDigit(self->hundredths, 1000);
self->text[4] = FPSDigit(self->hundredths, 100);
self->text[5] = '.';
self->text[6] = FPSDigit(self->hundredths, 10);
self->text[7] = FPSDigit(self->hundredths, 1);
self->text[8] = ' ';
self->text[9] = 'F';
self->text[10] = 'P';
self->text[11] = 'S';
self->text[12] = '\0';
self->lastUpdate.time = self->now;
self->lastUpdate.frame = self->frame;
return TRUE;
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_AllocSurfaceID --
*
* Allocate the next available surface ID.
*
* XXX: This is a trivial implementation which just returns
* an incrementing integer.
*
* Results:
* Returns an unused sid.
*
* Side effects:
* Marks this sid as used.
*
*----------------------------------------------------------------------
*/
uint32
SVGA3DUtil_AllocSurfaceID(void)
{
static uint32 nextSid = 0;
return nextSid++;
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_AllocDMABuffer --
*
* Allocate a buffer for DMA operations. Returns both a pointer
* (for us to use) and an SVGAGuestPtr (for the SVGA3D device to
* use).
*
* XXX: This is a trivial implementation which just returns
* consecutive addresses in the framebuffer.
*
* Results:
* Returns a local pointer and an SVGAGuestPtr to unused memory.
*
* Side effects:
* Allocates memory.
*
*----------------------------------------------------------------------
*/
void *
SVGA3DUtil_AllocDMABuffer(uint32 size, // IN
SVGAGuestPtr *ptr) // OUT
{
static SVGAGuestPtr nextPtr = { SVGA_GMR_FRAMEBUFFER, 0 };
*ptr = nextPtr;
nextPtr.offset += size;
return gSVGA.fbMem + ptr->offset;
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_DefineSurface2D --
*
* This is a simplified version of SVGA3D_BeginDefineSurface(),
* which does not support cube maps, mipmaps, or volume textures.
*
* Results:
* Returns the new surface ID.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
uint32
SVGA3DUtil_DefineSurface2D(uint32 width, // IN
uint32 height, // IN
SVGA3dSurfaceFormat format) // IN
{
uint32 sid;
SVGA3dSize *mipSizes;
SVGA3dSurfaceFace *faces;
sid = SVGA3DUtil_AllocSurfaceID();
SVGA3D_BeginDefineSurface(sid, 0, format, &faces, &mipSizes, 1);
faces[0].numMipLevels = 1;
mipSizes[0].width = width;
mipSizes[0].height = height;
mipSizes[0].depth = 1;
SVGA_FIFOCommitAll();
return sid;
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_SurfaceDMA2D --
*
* This is a simplified version of SVGA3D_BeginSurfaceDMA(),
* which copies a single 2D rectangle rooted at 0,0. It does
* not support volume textures, mipmaps, cube maps, or guest
* images with non-default pitch.
*
* Results:
* None.
*
* Side effects:
* Begins an asynchronous DMA operation.
*
*----------------------------------------------------------------------
*/
void
SVGA3DUtil_SurfaceDMA2D(uint32 sid, // IN
SVGAGuestPtr *guestPtr, // IN
SVGA3dTransferType transfer, // IN
uint32 width, // IN
uint32 height) // IN
{
SVGA3dCopyBox *boxes;
SVGA3dGuestImage guestImage;
SVGA3dSurfaceImageId hostImage = { sid };
guestImage.ptr = *guestPtr;
guestImage.pitch = 0;
SVGA3D_BeginSurfaceDMA(&guestImage, &hostImage, transfer, &boxes, 1);
boxes[0].w = width;
boxes[0].h = height;
boxes[0].d = 1;
SVGA_FIFOCommitAll();
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_DefineStaticBuffer --
*
* This is a simplified function which defines a 1 dimensional
* surface with the format SVGA3D_BUFFER, copies the supplied
* data into a new DMA buffer, and begins a DMA transfer into
* the new surface.
*
* Results:
* Returns the new surface ID.
*
* Side effects:
* Allocates a surface.
* Allocates a DMA buffer.
* Begins an asynchronous DMA operation.
*
*----------------------------------------------------------------------
*/
uint32
SVGA3DUtil_DefineStaticBuffer(const void *data, // IN
uint32 size) // IN
{
void *buffer;
SVGAGuestPtr gPtr;
uint32 sid = SVGA3DUtil_DefineSurface2D(size, 1, SVGA3D_BUFFER);
buffer = SVGA3DUtil_AllocDMABuffer(size, &gPtr);
memcpy(buffer, data, size);
SVGA3DUtil_SurfaceDMA2D(sid, &gPtr, SVGA3D_WRITE_HOST_VRAM, size, 1);
return sid;
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_LoadCompressedBuffer --
*
* This is a simplified function which defines a 1 dimensional
* surface with the format SVGA3D_BUFFER, decompresses a DataFile
* object into a new DMA buffer, and begins a DMA transfer into
* the new surface.
*
* Results:
* Returns the new surface ID. Optionally returns the
* decompressed size of the buffer.
*
* Side effects:
* Allocates a surface.
* Allocates a DMA buffer.
* Begins an asynchronous DMA operation.
*
*----------------------------------------------------------------------
*/
uint32
SVGA3DUtil_LoadCompressedBuffer(const DataFile *file, // IN
uint32 *pSize) // OUT (optional)
{
void *buffer;
SVGAGuestPtr gPtr;
uint32 size = DataFile_GetDecompressedSize(file);
uint32 sid = SVGA3DUtil_DefineSurface2D(size, 1, SVGA3D_BUFFER);
buffer = SVGA3DUtil_AllocDMABuffer(size, &gPtr);
DataFile_Decompress(file, buffer, size);
SVGA3DUtil_SurfaceDMA2D(sid, &gPtr, SVGA3D_WRITE_HOST_VRAM, size, 1);
if (pSize) {
*pSize = size;
}
return sid;
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_ClearFullscreen --
*
* This is a simplified version of SVGA3D_BeginClear(), which
* assumes we're in full-screen mode and that the entire render
* target needs to be cleared.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
SVGA3DUtil_ClearFullscreen(uint32 cid, // IN
SVGA3dClearFlag flags, // IN
uint32 color, // IN
float depth, // IN
uint32 stencil) // IN
{
SVGA3dRect *rect;
SVGA3D_BeginClear(cid, flags, color, depth, stencil, &rect, 1);
memset(rect, 0, sizeof *rect);
rect->w = gSVGA.width;
rect->h = gSVGA.height;
SVGA_FIFOCommitAll();
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_AllocDMAPool --
*
* This is a simple example implementation of a DMA buffer pool-
* a collection of identically-sized DMA buffers which are
* allocated once and recycled.
*
* Results:
* Initializes the provided DMAPool structure.
*
* Side effects:
* Allocates DMA memory.
*
*----------------------------------------------------------------------
*/
void
SVGA3DUtil_AllocDMAPool(DMAPool *self, // OUT
uint32 bufferSize, // IN
uint32 numBuffers) // IN
{
int i;
memset(self, 0, sizeof *self);
self->bufferSize = bufferSize;
self->numBuffers = numBuffers;
if (numBuffers > MAX_DMA_POOL_BUFFERS) {
SVGA_Panic("DMA pool larger than MAX_DMA_POOL_BUFFERS");
}
for (i = 0; i < numBuffers; i++) {
self->buffers[i].pool = self;
self->buffers[i].buffer = SVGA3DUtil_AllocDMABuffer(bufferSize,
&self->buffers[i].ptr);
self->buffers[i].next = self->freeList;
self->freeList = &self->buffers[i];
}
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_DMAPoolGetBuffer --
*
* Retrieve an available buffer from a DMAPool. This
* returns the first available buffer from our freelist.
*
* If no buffers are on the freelist, we use Sync and
* AsyncCall to give them a chance to become available.
*
* If that fails, there must have been a buffer leak. We panic.
*
* Results:
* Returns an available DMAPoolBuffer.
*
* Side effects:
* May Sync. May run AsyncCall callbacks.
*
*----------------------------------------------------------------------
*/
DMAPoolBuffer *
SVGA3DUtil_DMAPoolGetBuffer(DMAPool *self) // IN/OUT
{
DMAPoolBuffer *buffer;
if (!self->freeList) {
SVGA_SyncToFence(SVGA_InsertFence());
SVGA3DUtil_AsyncCall(NULL, NULL);
}
buffer = self->freeList;
if (!buffer) {
SVGA_Panic("No DMA buffers available from pool");
}
self->freeList = buffer->next;
return buffer;
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_DMAPoolFreeBuffer --
*
* Recycle an old DMAPool buffer. This function should be called
* only once the DMA transfer has been completed by the device.
* You must use the FIFO fence mechanism to guarantee this.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
SVGA3DUtil_DMAPoolFreeBuffer(DMAPoolBuffer *buffer) // IN
{
buffer->next = buffer->pool->freeList;
buffer->pool->freeList = buffer;
}
/*
*----------------------------------------------------------------------
*
* SVGA3DUtil_SetShaderConstMatrix --
*
* This is a simple wrapper around SVGA3D_SetShaderConst which
* makes it easier to set a constant matrix.
*
* Each column of the matrix is stored separately, in four
* consecutive float4 vectors.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
SVGA3DUtil_SetShaderConstMatrix(uint32 cid, // IN
uint32 reg, // IN
SVGA3dShaderType type, // IN
const float *matrix) // IN
{
int col;
for (col = 0; col < 4; col++) {
float vector[4];
vector[0] = matrix[col + 0];
vector[1] = matrix[col + 4];
vector[2] = matrix[col + 8];
vector[3] = matrix[col + 12];
SVGA3D_SetShaderConst(cid, reg + col, type,
SVGA3D_CONST_TYPE_FLOAT, vector);
}
}