/**********************************************************
 * 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.
 *
 **********************************************************/

/**
 * svga_cmd.c --
 *
 *      Command construction utility for the SVGA3D protocol used by
 *      the VMware SVGA device, based on the svgautil library.
 */

#include "svga_winsys.h"
#include "svga_resource_buffer.h"
#include "svga_resource_texture.h"
#include "svga_surface.h"
#include "svga_cmd.h"

/*
 *----------------------------------------------------------------------
 *
 * surface_to_surfaceid --
 *
 *      Utility function for surface ids.
 *      Can handle null surface. Does a surface_reallocation so you need
 *      to have allocated the fifo space before converting.
 *
 * Results:
 *      id is filled out.
 *
 * Side effects:
 *      One surface relocation is performed for texture handle.
 *
 *----------------------------------------------------------------------
 */

static INLINE
void surface_to_surfaceid(struct svga_winsys_context *swc, // IN
                          struct pipe_surface *surface,    // IN
                          SVGA3dSurfaceImageId *id,        // OUT
                          unsigned flags)                  // IN
{
   if(surface) {
      struct svga_surface *s = svga_surface(surface);
      swc->surface_relocation(swc, &id->sid, s->handle, flags);
      id->face = s->real_face; /* faces have the same order */
      id->mipmap = s->real_level;
   }
   else {
      swc->surface_relocation(swc, &id->sid, NULL, flags);
      id->face = 0;
      id->mipmap = 0;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_FIFOReserve --
 *
 *      Reserve space for an SVGA3D FIFO command.
 *
 *      The 2D SVGA commands have been around for a while, so they
 *      have a rather asymmetric structure. The SVGA3D protocol is
 *      more uniform: each command begins with a header containing the
 *      command number and the full size.
 *
 *      This is a convenience wrapper around SVGA_FIFOReserve. We
 *      reserve space for the whole command, and write the header.
 *
 *      This function must be paired with SVGA_FIFOCommitAll().
 *
 * Results:
 *      Returns a pointer to the space reserved for command-specific
 *      data. It must be 'cmdSize' bytes long.
 *
 * Side effects:
 *      Begins a FIFO reservation.
 *
 *----------------------------------------------------------------------
 */

void *
SVGA3D_FIFOReserve(struct svga_winsys_context *swc,
                   uint32 cmd,       // IN
                   uint32 cmdSize,   // IN
                   uint32 nr_relocs) // IN
{
   SVGA3dCmdHeader *header;

   header = swc->reserve(swc, sizeof *header + cmdSize, nr_relocs);
   if(!header)
      return NULL;

   header->id = cmd;
   header->size = cmdSize;

   return &header[1];
}


void
SVGA_FIFOCommitAll(struct svga_winsys_context *swc)
{
   swc->commit(swc);
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_DefineContext --
 *
 *      Create a new context, to be referred to with the provided ID.
 *
 *      Context objects encapsulate all render state, and shader
 *      objects are per-context.
 *
 *      Surfaces are not per-context. The same surface can be shared
 *      between multiple contexts, and surface operations can occur
 *      without a context.
 *
 *      If the provided context ID already existed, it is redefined.
 *
 *      Context IDs are arbitrary small non-negative integers,
 *      global to the entire SVGA device.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_DefineContext(struct svga_winsys_context *swc)  // IN
{
   SVGA3dCmdDefineContext *cmd;

   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_CONTEXT_DEFINE, sizeof *cmd, 0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;

   swc->commit(swc);
   
   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_DestroyContext --
 *
 *      Delete a context created with SVGA3D_DefineContext.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_DestroyContext(struct svga_winsys_context *swc)  // IN
{
   SVGA3dCmdDestroyContext *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_CONTEXT_DESTROY, sizeof *cmd, 0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;
   
   cmd->cid = swc->cid;
   
   swc->commit(swc);
   
   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_BeginDefineSurface --
 *
 *      Begin a SURFACE_DEFINE command. This reserves space for it in
 *      the FIFO, and returns pointers to the command's faces and
 *      mipsizes arrays.
 *
 *      This function must be paired with SVGA_FIFOCommitAll().
 *      The faces and mipSizes arrays are initialized to zero.
 *
 *      This creates a "surface" object in the SVGA3D device,
 *      with the provided surface ID (sid). Surfaces are generic
 *      containers for host VRAM objects like textures, vertex
 *      buffers, and depth/stencil buffers.
 *
 *      Surfaces are hierarchical:
 *
 *        - Surface may have multiple faces (for cube maps)
 *
 *          - Each face has a list of mipmap levels
 *
 *             - Each mipmap image may have multiple volume
 *               slices, if the image is three dimensional.
 *
 *                - Each slice is a 2D array of 'blocks'
 *
 *                   - Each block may be one or more pixels.
 *                     (Usually 1, more for DXT or YUV formats.)
 *
 *      Surfaces are generic host VRAM objects. The SVGA3D device
 *      may optimize surfaces according to the format they were
 *      created with, but this format does not limit the ways in
 *      which the surface may be used. For example, a depth surface
 *      can be used as a texture, or a floating point image may
 *      be used as a vertex buffer. Some surface usages may be
 *      lower performance, due to software emulation, but any
 *      usage should work with any surface.
 *
 *      If 'sid' is already defined, the old surface is deleted
 *      and this new surface replaces it.
 *
 *      Surface IDs are arbitrary small non-negative integers,
 *      global to the entire SVGA device.
 *
 * Results:
 *      Returns pointers to arrays allocated in the FIFO for 'faces'
 *      and 'mipSizes'.
 *
 * Side effects:
 *      Begins a FIFO reservation.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_BeginDefineSurface(struct svga_winsys_context *swc,
                          struct svga_winsys_surface *sid, // IN
                          SVGA3dSurfaceFlags flags,    // IN
                          SVGA3dSurfaceFormat format,  // IN
                          SVGA3dSurfaceFace **faces,   // OUT
                          SVGA3dSize **mipSizes,       // OUT
                          uint32 numMipSizes)          // IN
{
   SVGA3dCmdDefineSurface *cmd;

   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SURFACE_DEFINE, sizeof *cmd +
                            sizeof **mipSizes * numMipSizes, 1);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   swc->surface_relocation(swc, &cmd->sid, sid, SVGA_RELOC_WRITE);
   cmd->surfaceFlags = flags;
   cmd->format = format;

   *faces = &cmd->face[0];
   *mipSizes = (SVGA3dSize*) &cmd[1];

   memset(*faces, 0, sizeof **faces * SVGA3D_MAX_SURFACE_FACES);
   memset(*mipSizes, 0, sizeof **mipSizes * numMipSizes);
   
   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_DefineSurface2D --
 *
 *      This is a simplified version of SVGA3D_BeginDefineSurface(),
 *      which does not support cube maps, mipmaps, or volume textures.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_DefineSurface2D(struct svga_winsys_context *swc,    // IN
                       struct svga_winsys_surface *sid, // IN
                       uint32 width,                // IN
                       uint32 height,               // IN
                       SVGA3dSurfaceFormat format)  // IN
{
   SVGA3dSize *mipSizes;
   SVGA3dSurfaceFace *faces;
   enum pipe_error ret;

   ret = SVGA3D_BeginDefineSurface(swc,
                                   sid, 0, format, &faces, &mipSizes, 1);
   if(ret != PIPE_OK)
      return ret;

   faces[0].numMipLevels = 1;

   mipSizes[0].width = width;
   mipSizes[0].height = height;
   mipSizes[0].depth = 1;
 
   swc->commit(swc);;
   
   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_DestroySurface --
 *
 *      Release the host VRAM encapsulated by a particular surface ID.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_DestroySurface(struct svga_winsys_context *swc,
                      struct svga_winsys_surface *sid)  // IN
{
   SVGA3dCmdDestroySurface *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SURFACE_DESTROY, sizeof *cmd, 1);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;
   
   swc->surface_relocation(swc, &cmd->sid, sid, SVGA_RELOC_READ);
   swc->commit(swc);;
   
   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SurfaceDMA--
 *
 *      Emit a SURFACE_DMA command.
 *
 *      When the SVGA3D device asynchronously processes this FIFO
 *      command, a DMA operation is performed between host VRAM and
 *      a generic SVGAGuestPtr. The guest pointer may refer to guest
 *      VRAM (provided by the SVGA PCI device) or to guest system
 *      memory that has been set up as a Guest Memory Region (GMR)
 *      by the SVGA device.
 *
 *      The guest's DMA buffer must remain valid (not freed, paged out,
 *      or overwritten) until the host has finished processing this
 *      command. The guest can determine that the host has finished
 *      by using the SVGA device's FIFO Fence mechanism.
 *
 *      The guest's image buffer can be an arbitrary size and shape.
 *      Guest image data is interpreted according to the SVGA3D surface
 *      format specified when the surface was defined.
 *
 *      The caller may optionally define the guest image's pitch.
 *      guestImage->pitch can either be zero (assume image is tightly
 *      packed) or it must be the number of bytes between vertically
 *      adjacent image blocks.
 *
 *      The provided copybox list specifies which regions of the source
 *      image are to be copied, and where they appear on the destination.
 *
 *      NOTE: srcx/srcy are always on the guest image and x/y are
 *      always on the host image, regardless of the actual transfer
 *      direction!
 *
 *      For efficiency, the SVGA3D device is free to copy more data
 *      than specified. For example, it may round copy boxes outwards
 *      such that they lie on particular alignment boundaries.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SurfaceDMA(struct svga_winsys_context *swc,
                  struct svga_transfer *st,         // IN
                  SVGA3dTransferType transfer,      // IN
                  const SVGA3dCopyBox *boxes,       // IN
                  uint32 numBoxes,                  // IN
                  SVGA3dSurfaceDMAFlags flags)      // IN
{
   struct svga_texture *texture = svga_texture(st->base.resource); 
   SVGA3dCmdSurfaceDMA *cmd;
   SVGA3dCmdSurfaceDMASuffix *pSuffix;
   uint32 boxesSize = sizeof *boxes * numBoxes;
   unsigned region_flags;
   unsigned surface_flags;
   
   if(transfer == SVGA3D_WRITE_HOST_VRAM) {
      region_flags = SVGA_RELOC_READ;
      surface_flags = SVGA_RELOC_WRITE;
   }
   else if(transfer == SVGA3D_READ_HOST_VRAM) {
      region_flags = SVGA_RELOC_WRITE;
      surface_flags = SVGA_RELOC_READ;
   }
   else {
      assert(0);
      return PIPE_ERROR_BAD_INPUT;
   }
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SURFACE_DMA,
                            sizeof *cmd + boxesSize + sizeof *pSuffix,
                            2);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   swc->region_relocation(swc, &cmd->guest.ptr, st->hwbuf, 0, region_flags);
   cmd->guest.pitch = st->base.stride;

   swc->surface_relocation(swc, &cmd->host.sid, texture->handle, surface_flags);
   cmd->host.face = st->face; /* PIPE_TEX_FACE_* and SVGA3D_CUBEFACE_* match */
   cmd->host.mipmap = st->base.level;

   cmd->transfer = transfer;

   memcpy(&cmd[1], boxes, boxesSize);
   
   pSuffix = (SVGA3dCmdSurfaceDMASuffix *)((uint8_t*)cmd + sizeof *cmd + boxesSize);
   pSuffix->suffixSize = sizeof *pSuffix;
   pSuffix->maximumOffset = st->hw_nblocksy*st->base.stride;
   pSuffix->flags = flags;

   swc->commit(swc);

   return PIPE_OK;
}


enum pipe_error
SVGA3D_BufferDMA(struct svga_winsys_context *swc,
                 struct svga_winsys_buffer *guest,
                 struct svga_winsys_surface *host,
                 SVGA3dTransferType transfer,      // IN
                 uint32 size,                      // IN
                 uint32 guest_offset,              // IN
                 uint32 host_offset,               // IN
                 SVGA3dSurfaceDMAFlags flags)      // IN
{
   SVGA3dCmdSurfaceDMA *cmd;
   SVGA3dCopyBox *box;
   SVGA3dCmdSurfaceDMASuffix *pSuffix;
   unsigned region_flags;
   unsigned surface_flags;
   
   if(transfer == SVGA3D_WRITE_HOST_VRAM) {
      region_flags = SVGA_RELOC_READ;
      surface_flags = SVGA_RELOC_WRITE;
   }
   else if(transfer == SVGA3D_READ_HOST_VRAM) {
      region_flags = SVGA_RELOC_WRITE;
      surface_flags = SVGA_RELOC_READ;
   }
   else {
      assert(0);
      return PIPE_ERROR_BAD_INPUT;
   }
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SURFACE_DMA,
                            sizeof *cmd + sizeof *box + sizeof *pSuffix,
                            2);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   swc->region_relocation(swc, &cmd->guest.ptr, guest, 0, region_flags);
   cmd->guest.pitch = 0;

   swc->surface_relocation(swc, &cmd->host.sid, host, surface_flags);
   cmd->host.face = 0;
   cmd->host.mipmap = 0;

   cmd->transfer = transfer;

   box = (SVGA3dCopyBox *)&cmd[1];
   box->x = host_offset;
   box->y = 0;
   box->z = 0;
   box->w = size;
   box->h = 1;
   box->d = 1;
   box->srcx = guest_offset;
   box->srcy = 0;
   box->srcz = 0;
   
   pSuffix = (SVGA3dCmdSurfaceDMASuffix *)((uint8_t*)cmd + sizeof *cmd + sizeof *box);
   pSuffix->suffixSize = sizeof *pSuffix;
   pSuffix->maximumOffset = guest_offset + size;
   pSuffix->flags = flags;

   swc->commit(swc);

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SetRenderTarget --
 *
 *      Bind a surface object to a particular render target attachment
 *      point on the current context. Render target attachment points
 *      exist for color buffers, a depth buffer, and a stencil buffer.
 *
 *      The SVGA3D device is quite lenient about the types of surfaces
 *      that may be used as render targets. The color buffers must
 *      all be the same size, but the depth and stencil buffers do not
 *      have to be the same size as the color buffer. All attachments
 *      are optional.
 *
 *      Some combinations of render target formats may require software
 *      emulation, depending on the capabilities of the host graphics
 *      API and graphics hardware.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SetRenderTarget(struct svga_winsys_context *swc,
                       SVGA3dRenderTargetType type,   // IN
                       struct pipe_surface *surface)  // IN
{
   SVGA3dCmdSetRenderTarget *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SETRENDERTARGET, sizeof *cmd, 1);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;


   cmd->cid = swc->cid;

   cmd->type = type;

   surface_to_surfaceid(swc, surface, &cmd->target, SVGA_RELOC_WRITE);

   swc->commit(swc);

   return PIPE_OK;
}






/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_DefineShader --
 *
 *      Upload the bytecode for a new shader. The bytecode is "SVGA3D
 *      format", which is theoretically a binary-compatible superset
 *      of Microsoft's DirectX shader bytecode. In practice, the
 *      SVGA3D bytecode doesn't yet have any extensions to DirectX's
 *      bytecode format.
 *
 *      The SVGA3D device supports shader models 1.1 through 2.0.
 *
 *      The caller chooses a shader ID (small positive integer) by
 *      which this shader will be identified in future commands. This
 *      ID is in a namespace which is per-context and per-shader-type.
 *
 *      'bytecodeLen' is specified in bytes. It must be a multiple of 4.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_DefineShader(struct svga_winsys_context *swc,
                    uint32 shid,                  // IN
                    SVGA3dShaderType type,        // IN
                    const uint32 *bytecode,       // IN
                    uint32 bytecodeLen)           // IN
{
   SVGA3dCmdDefineShader *cmd;

   assert(bytecodeLen % 4 == 0);

   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SHADER_DEFINE, sizeof *cmd + bytecodeLen,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->shid = shid;
   cmd->type = type;
   memcpy(&cmd[1], bytecode, bytecodeLen);
   swc->commit(swc);

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_DestroyShader --
 *
 *      Delete a shader that was created by SVGA3D_DefineShader. If
 *      the shader was the current vertex or pixel shader for its
 *      context, rendering results are undefined until a new shader is
 *      bound.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_DestroyShader(struct svga_winsys_context *swc,
                     uint32 shid,            // IN
                     SVGA3dShaderType type)  // IN
{
   SVGA3dCmdDestroyShader *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SHADER_DESTROY, sizeof *cmd,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->shid = shid;
   cmd->type = type;
   swc->commit(swc);

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SetShaderConst --
 *
 *      Set the value of a shader constant.
 *
 *      Shader constants are analogous to uniform variables in GLSL,
 *      except that they belong to the render context rather than to
 *      an individual shader.
 *
 *      Constants may have one of three types: A 4-vector of floats,
 *      a 4-vector of integers, or a single boolean flag.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SetShaderConst(struct svga_winsys_context *swc,
                      uint32 reg,                   // IN
                      SVGA3dShaderType type,        // IN
                      SVGA3dShaderConstType ctype,  // IN
                      const void *value)            // IN
{
   SVGA3dCmdSetShaderConst *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SET_SHADER_CONST, sizeof *cmd,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->reg = reg;
   cmd->type = type;
   cmd->ctype = ctype;

   switch (ctype) {

   case SVGA3D_CONST_TYPE_FLOAT:
   case SVGA3D_CONST_TYPE_INT:
      memcpy(&cmd->values, value, sizeof cmd->values);
      break;

   case SVGA3D_CONST_TYPE_BOOL:
      memset(&cmd->values, 0, sizeof cmd->values);
      cmd->values[0] = *(uint32*)value;
      break;

   default:
      assert(0);
      break;

   }
   swc->commit(swc);

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SetShaderConsts --
 *
 *      Set the value of successive shader constants.
 *
 *      Shader constants are analogous to uniform variables in GLSL,
 *      except that they belong to the render context rather than to
 *      an individual shader.
 *
 *      Constants may have one of three types: A 4-vector of floats,
 *      a 4-vector of integers, or a single boolean flag.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SetShaderConsts(struct svga_winsys_context *swc,
                        uint32 reg,                   // IN
                        uint32 numRegs,               // IN
                        SVGA3dShaderType type,        // IN
                        SVGA3dShaderConstType ctype,  // IN
                        const void *values)           // IN
{
   SVGA3dCmdSetShaderConst *cmd;

   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SET_SHADER_CONST,
                            sizeof *cmd + (numRegs - 1) * sizeof cmd->values,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->reg = reg;
   cmd->type = type;
   cmd->ctype = ctype;

   memcpy(&cmd->values, values, numRegs * sizeof cmd->values);

   swc->commit(swc);

   return PIPE_OK;
}





/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SetShader --
 *
 *      Switch active shaders. This binds a new vertex or pixel shader
 *      to the specified context.
 *
 *      A shader ID of SVGA3D_INVALID_ID unbinds any shader, switching
 *      back to the fixed function vertex or pixel pipeline.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SetShader(struct svga_winsys_context *swc,
                 SVGA3dShaderType type,  // IN
                 uint32 shid)            // IN
{
   SVGA3dCmdSetShader *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SET_SHADER, sizeof *cmd,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;
   
   cmd->cid = swc->cid;
   cmd->type = type;
   cmd->shid = shid;
   swc->commit(swc);

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_BeginClear --
 *
 *      Begin a CLEAR command. This reserves space for it in the FIFO,
 *      and returns a pointer to the command's rectangle array.  This
 *      function must be paired with SVGA_FIFOCommitAll().
 *
 *      Clear is a rendering operation which fills a list of
 *      rectangles with constant values on all render target types
 *      indicated by 'flags'.
 *
 *      Clear is not affected by clipping, depth test, or other
 *      render state which affects the fragment pipeline.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      May write to attached render target surfaces.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_BeginClear(struct svga_winsys_context *swc,
                  SVGA3dClearFlag flags,  // IN
                  uint32 color,           // IN
                  float depth,            // IN
                  uint32 stencil,         // IN
                  SVGA3dRect **rects,     // OUT
                  uint32 numRects)        // IN
{
   SVGA3dCmdClear *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_CLEAR, 
                            sizeof *cmd + sizeof **rects * numRects,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->clearFlag = flags;
   cmd->color = color;
   cmd->depth = depth;
   cmd->stencil = stencil;
   *rects = (SVGA3dRect*) &cmd[1];

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_ClearRect --
 *
 *      This is a simplified version of SVGA3D_BeginClear().
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_ClearRect(struct svga_winsys_context *swc,
                 SVGA3dClearFlag flags,  // IN
                 uint32 color,           // IN
                 float depth,            // IN
                 uint32 stencil,         // IN
                 uint32 x,               // IN
                 uint32 y,               // IN
                 uint32 w,               // IN
                 uint32 h)               // IN
{
   SVGA3dRect *rect;
   enum pipe_error ret;

   ret = SVGA3D_BeginClear(swc, flags, color, depth, stencil, &rect, 1);
   if(ret != PIPE_OK)
      return PIPE_ERROR_OUT_OF_MEMORY;

   memset(rect, 0, sizeof *rect);
   rect->x = x;
   rect->y = y;
   rect->w = w;
   rect->h = h;
   swc->commit(swc);

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_BeginDrawPrimitives --
 *
 *      Begin a DRAW_PRIMITIVES command. This reserves space for it in
 *      the FIFO, and returns a pointer to the command's arrays.
 *      This function must be paired with SVGA_FIFOCommitAll().
 *
 *      Drawing commands consist of two variable-length arrays:
 *      SVGA3dVertexDecl elements declare a set of vertex buffers to
 *      use while rendering, and SVGA3dPrimitiveRange elements specify
 *      groups of primitives each with an optional index buffer.
 *
 *      The decls and ranges arrays are initialized to zero.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      May write to attached render target surfaces.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_BeginDrawPrimitives(struct svga_winsys_context *swc,
                           SVGA3dVertexDecl **decls,      // OUT
                           uint32 numVertexDecls,         // IN
                           SVGA3dPrimitiveRange **ranges, // OUT
                           uint32 numRanges)              // IN
{
   SVGA3dCmdDrawPrimitives *cmd;
   SVGA3dVertexDecl *declArray;
   SVGA3dPrimitiveRange *rangeArray;
   uint32 declSize = sizeof **decls * numVertexDecls;
   uint32 rangeSize = sizeof **ranges * numRanges;

   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_DRAW_PRIMITIVES, 
                            sizeof *cmd + declSize + rangeSize,
                            numVertexDecls + numRanges);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->numVertexDecls = numVertexDecls;
   cmd->numRanges = numRanges;

   declArray = (SVGA3dVertexDecl*) &cmd[1];
   rangeArray = (SVGA3dPrimitiveRange*) &declArray[numVertexDecls];

   memset(declArray, 0, declSize);
   memset(rangeArray, 0, rangeSize);

   *decls = declArray;
   *ranges = rangeArray;

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_BeginSurfaceCopy --
 *
 *      Begin a SURFACE_COPY command. This reserves space for it in
 *      the FIFO, and returns a pointer to the command's arrays.  This
 *      function must be paired with SVGA_FIFOCommitAll().
 *
 *      The box array is initialized with zeroes.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Asynchronously copies a list of boxes from surface to surface.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_BeginSurfaceCopy(struct svga_winsys_context *swc,
                        struct pipe_surface *src,    // IN
                        struct pipe_surface *dest,   // IN
                        SVGA3dCopyBox **boxes,       // OUT
                        uint32 numBoxes)             // IN
{
   SVGA3dCmdSurfaceCopy *cmd;
   uint32 boxesSize = sizeof **boxes * numBoxes;

   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SURFACE_COPY, sizeof *cmd + boxesSize,
                            2);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   surface_to_surfaceid(swc, src, &cmd->src, SVGA_RELOC_READ);
   surface_to_surfaceid(swc, dest, &cmd->dest, SVGA_RELOC_WRITE);
   *boxes = (SVGA3dCopyBox*) &cmd[1];

   memset(*boxes, 0, boxesSize);

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SurfaceStretchBlt --
 *
 *      Issue a SURFACE_STRETCHBLT command: an asynchronous
 *      surface-to-surface blit, with scaling.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Asynchronously copies one box from surface to surface.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SurfaceStretchBlt(struct svga_winsys_context *swc,
                         struct pipe_surface *src,    // IN
                         struct pipe_surface *dest,   // IN
                         SVGA3dBox *boxSrc,           // IN
                         SVGA3dBox *boxDest,          // IN
                         SVGA3dStretchBltMode mode)   // IN
{
   SVGA3dCmdSurfaceStretchBlt *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SURFACE_STRETCHBLT, sizeof *cmd,
                            2);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   surface_to_surfaceid(swc, src, &cmd->src, SVGA_RELOC_READ);
   surface_to_surfaceid(swc, dest, &cmd->dest, SVGA_RELOC_WRITE);
   cmd->boxSrc = *boxSrc;
   cmd->boxDest = *boxDest;
   cmd->mode = mode;
   swc->commit(swc);

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SetViewport --
 *
 *      Set the current context's viewport rectangle. The viewport
 *      is clipped to the dimensions of the current render target,
 *      then all rendering is clipped to the viewport.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SetViewport(struct svga_winsys_context *swc,
                   SVGA3dRect *rect)  // IN
{
   SVGA3dCmdSetViewport *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SETVIEWPORT, sizeof *cmd,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->rect = *rect;
   swc->commit(swc);

   return PIPE_OK;
}




/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SetScissorRect --
 *
 *      Set the current context's scissor rectangle. If scissor
 *      is enabled then all rendering is clipped to the scissor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SetScissorRect(struct svga_winsys_context *swc,
                      SVGA3dRect *rect)  // IN
{
   SVGA3dCmdSetScissorRect *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SETSCISSORRECT, sizeof *cmd,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->rect = *rect;
   swc->commit(swc);

   return PIPE_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SetClipPlane --
 *
 *      Set one of the current context's clip planes. If the clip
 *      plane is enabled then all 3d rendering is clipped to against
 *      the plane.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SetClipPlane(struct svga_winsys_context *swc,
                    uint32 index, const float *plane)
{
   SVGA3dCmdSetClipPlane *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SETCLIPPLANE, sizeof *cmd,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->index = index;
   cmd->plane[0] = plane[0];
   cmd->plane[1] = plane[1];
   cmd->plane[2] = plane[2];
   cmd->plane[3] = plane[3];
   swc->commit(swc);

   return PIPE_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_SetZRange --
 *
 *      Set the range of the depth buffer to use. 'min' and 'max'
 *      are values between 0.0 and 1.0.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_SetZRange(struct svga_winsys_context *swc,
                 float zMin,  // IN
                 float zMax)  // IN
{
   SVGA3dCmdSetZRange *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SETZRANGE, sizeof *cmd,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->zRange.min = zMin;
   cmd->zRange.max = zMax;
   swc->commit(swc);

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_BeginSetTextureState --
 *
 *      Begin a SETTEXTURESTATE command. This reserves space for it in
 *      the FIFO, and returns a pointer to the command's texture state
 *      array.  This function must be paired with SVGA_FIFOCommitAll().
 *
 *      This command sets rendering state which is per-texture-unit.
 *
 *      XXX: Individual texture states need documentation. However,
 *           they are very similar to the texture states defined by
 *           Direct3D. The D3D documentation is a good starting point
 *           for understanding SVGA3D texture states.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_BeginSetTextureState(struct svga_winsys_context *swc,
                            SVGA3dTextureState **states,  // OUT
                            uint32 numStates)             // IN
{
   SVGA3dCmdSetTextureState *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SETTEXTURESTATE, 
                            sizeof *cmd + sizeof **states * numStates,
                            numStates);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   *states = (SVGA3dTextureState*) &cmd[1];

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_BeginSetRenderState --
 *
 *      Begin a SETRENDERSTATE command. This reserves space for it in
 *      the FIFO, and returns a pointer to the command's texture state
 *      array.  This function must be paired with SVGA_FIFOCommitAll().
 *
 *      This command sets rendering state which is global to the context.
 *
 *      XXX: Individual render states need documentation. However,
 *           they are very similar to the render states defined by
 *           Direct3D. The D3D documentation is a good starting point
 *           for understanding SVGA3D render states.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_BeginSetRenderState(struct svga_winsys_context *swc,
                           SVGA3dRenderState **states,  // OUT
                           uint32 numStates)            // IN
{
   SVGA3dCmdSetRenderState *cmd;
   
   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_SETRENDERSTATE, 
                            sizeof *cmd + sizeof **states * numStates,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   *states = (SVGA3dRenderState*) &cmd[1];

   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_BeginQuery--
 *
 *      Issues a SVGA_3D_CMD_BEGIN_QUERY command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Commits space in the FIFO memory.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_BeginQuery(struct svga_winsys_context *swc,
                  SVGA3dQueryType type) // IN
{
   SVGA3dCmdBeginQuery *cmd;

   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_BEGIN_QUERY,
                            sizeof *cmd,
                            0);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->type = type;

   swc->commit(swc);
   
   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_EndQuery--
 *
 *      Issues a SVGA_3D_CMD_END_QUERY command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Commits space in the FIFO memory.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_EndQuery(struct svga_winsys_context *swc,
                SVGA3dQueryType type,              // IN
                struct svga_winsys_buffer *buffer) // IN/OUT
{
   SVGA3dCmdEndQuery *cmd;

   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_END_QUERY, 
                            sizeof *cmd,
                            1);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->type = type;

   swc->region_relocation(swc, &cmd->guestResult, buffer, 0,
                          SVGA_RELOC_WRITE);

   swc->commit(swc);
   
   return PIPE_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SVGA3D_WaitForQuery--
 *
 *      Issues a SVGA_3D_CMD_WAIT_FOR_QUERY command.  This reserves space
 *      for it in the FIFO.  This doesn't actually wait for the query to
 *      finish but instead tells the host to start a wait at the driver
 *      level.  The caller can wait on the status variable in the
 *      guestPtr memory or send an insert fence instruction after this
 *      command and wait on the fence.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Commits space in the FIFO memory.
 *
 *----------------------------------------------------------------------
 */

enum pipe_error
SVGA3D_WaitForQuery(struct svga_winsys_context *swc,
                    SVGA3dQueryType type,              // IN
                    struct svga_winsys_buffer *buffer) // IN/OUT
{
   SVGA3dCmdWaitForQuery *cmd;

   cmd = SVGA3D_FIFOReserve(swc,
                            SVGA_3D_CMD_WAIT_FOR_QUERY, 
                            sizeof *cmd,
                            1);
   if(!cmd)
      return PIPE_ERROR_OUT_OF_MEMORY;

   cmd->cid = swc->cid;
   cmd->type = type;
   
   swc->region_relocation(swc, &cmd->guestResult, buffer, 0,
                          SVGA_RELOC_WRITE);

   swc->commit(swc);
   
   return PIPE_OK;
}
