/**********************************************************
* 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.
*
**********************************************************/
#include "svga_cmd.h"
#include "util/u_inlines.h"
#include "util/u_prim.h"
#include "indices/u_indices.h"
#include "svga_hw_reg.h"
#include "svga_draw.h"
#include "svga_draw_private.h"
#include "svga_context.h"
#include "svga_shader.h"
#define DBG 0
static enum pipe_error
generate_indices(struct svga_hwtnl *hwtnl,
unsigned nr,
unsigned index_size,
u_generate_func generate, struct pipe_resource **out_buf)
{
struct pipe_context *pipe = &hwtnl->svga->pipe;
struct pipe_transfer *transfer;
unsigned size = index_size * nr;
struct pipe_resource *dst = NULL;
void *dst_map = NULL;
dst = pipe_buffer_create(pipe->screen, PIPE_BIND_INDEX_BUFFER,
PIPE_USAGE_IMMUTABLE, size);
if (!dst)
goto fail;
dst_map = pipe_buffer_map(pipe, dst, PIPE_TRANSFER_WRITE, &transfer);
if (!dst_map)
goto fail;
generate(0, nr, dst_map);
pipe_buffer_unmap(pipe, transfer);
*out_buf = dst;
return PIPE_OK;
fail:
if (dst_map)
pipe_buffer_unmap(pipe, transfer);
if (dst)
pipe->screen->resource_destroy(pipe->screen, dst);
return PIPE_ERROR_OUT_OF_MEMORY;
}
static boolean
compare(unsigned cached_nr, unsigned nr, unsigned type)
{
if (type == U_GENERATE_REUSABLE)
return cached_nr >= nr;
else
return cached_nr == nr;
}
static enum pipe_error
retrieve_or_generate_indices(struct svga_hwtnl *hwtnl,
enum pipe_prim_type prim,
unsigned gen_type,
unsigned gen_nr,
unsigned gen_size,
u_generate_func generate,
struct pipe_resource **out_buf)
{
enum pipe_error ret = PIPE_OK;
int i;
SVGA_STATS_TIME_PUSH(svga_sws(hwtnl->svga), SVGA_STATS_TIME_GENERATEINDICES);
for (i = 0; i < IDX_CACHE_MAX; i++) {
if (hwtnl->index_cache[prim][i].buffer != NULL &&
hwtnl->index_cache[prim][i].generate == generate) {
if (compare(hwtnl->index_cache[prim][i].gen_nr, gen_nr, gen_type)) {
pipe_resource_reference(out_buf,
hwtnl->index_cache[prim][i].buffer);
if (DBG)
debug_printf("%s retrieve %d/%d\n", __FUNCTION__, i, gen_nr);
goto done;
}
else if (gen_type == U_GENERATE_REUSABLE) {
pipe_resource_reference(&hwtnl->index_cache[prim][i].buffer,
NULL);
if (DBG)
debug_printf("%s discard %d/%d\n", __FUNCTION__,
i, hwtnl->index_cache[prim][i].gen_nr);
break;
}
}
}
if (i == IDX_CACHE_MAX) {
unsigned smallest = 0;
unsigned smallest_size = ~0;
for (i = 0; i < IDX_CACHE_MAX && smallest_size; i++) {
if (hwtnl->index_cache[prim][i].buffer == NULL) {
smallest = i;
smallest_size = 0;
}
else if (hwtnl->index_cache[prim][i].gen_nr < smallest) {
smallest = i;
smallest_size = hwtnl->index_cache[prim][i].gen_nr;
}
}
assert(smallest != IDX_CACHE_MAX);
pipe_resource_reference(&hwtnl->index_cache[prim][smallest].buffer,
NULL);
if (DBG)
debug_printf("%s discard smallest %d/%d\n", __FUNCTION__,
smallest, smallest_size);
i = smallest;
}
ret = generate_indices(hwtnl, gen_nr, gen_size, generate, out_buf);
if (ret != PIPE_OK)
goto done;
hwtnl->index_cache[prim][i].generate = generate;
hwtnl->index_cache[prim][i].gen_nr = gen_nr;
pipe_resource_reference(&hwtnl->index_cache[prim][i].buffer, *out_buf);
if (DBG)
debug_printf("%s cache %d/%d\n", __FUNCTION__,
i, hwtnl->index_cache[prim][i].gen_nr);
done:
SVGA_STATS_TIME_POP(svga_sws(hwtnl->svga));
return ret;
}
static enum pipe_error
simple_draw_arrays(struct svga_hwtnl *hwtnl,
enum pipe_prim_type prim, unsigned start, unsigned count,
unsigned start_instance, unsigned instance_count)
{
SVGA3dPrimitiveRange range;
unsigned hw_prim;
unsigned hw_count;
hw_prim = svga_translate_prim(prim, count, &hw_count);
if (hw_count == 0)
return PIPE_ERROR_BAD_INPUT;
range.primType = hw_prim;
range.primitiveCount = hw_count;
range.indexArray.surfaceId = SVGA3D_INVALID_ID;
range.indexArray.offset = 0;
range.indexArray.stride = 0;
range.indexWidth = 0;
range.indexBias = start;
/* Min/max index should be calculated prior to applying bias, so we
* end up with min_index = 0, max_index = count - 1 and everybody
* looking at those numbers knows to adjust them by
* range.indexBias.
*/
return svga_hwtnl_prim(hwtnl, &range, count,
0, count - 1, NULL,
start_instance, instance_count);
}
enum pipe_error
svga_hwtnl_draw_arrays(struct svga_hwtnl *hwtnl,
enum pipe_prim_type prim, unsigned start, unsigned count,
unsigned start_instance, unsigned instance_count)
{
enum pipe_prim_type gen_prim;
unsigned gen_size, gen_nr;
enum indices_mode gen_type;
u_generate_func gen_func;
enum pipe_error ret = PIPE_OK;
unsigned api_pv = hwtnl->api_pv;
struct svga_context *svga = hwtnl->svga;
SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_HWTNLDRAWARRAYS);
if (svga->curr.rast->templ.fill_front !=
svga->curr.rast->templ.fill_back) {
assert(hwtnl->api_fillmode == PIPE_POLYGON_MODE_FILL);
}
if (svga->curr.rast->templ.flatshade &&
svga->state.hw_draw.fs->constant_color_output) {
/* The fragment color is a constant, not per-vertex so the whole
* primitive will be the same color (except for possible blending).
* We can ignore the current provoking vertex state and use whatever
* the hardware wants.
*/
api_pv = hwtnl->hw_pv;
if (hwtnl->api_fillmode == PIPE_POLYGON_MODE_FILL) {
/* Do some simple primitive conversions to avoid index buffer
* generation below. Note that polygons and quads are not directly
* supported by the svga device. Also note, we can only do this
* for flat/constant-colored rendering because of provoking vertex.
*/
if (prim == PIPE_PRIM_POLYGON) {
prim = PIPE_PRIM_TRIANGLE_FAN;
}
else if (prim == PIPE_PRIM_QUADS && count == 4) {
prim = PIPE_PRIM_TRIANGLE_FAN;
}
}
}
if (svga_need_unfilled_fallback(hwtnl, prim)) {
/* Convert unfilled polygons into points, lines, triangles */
gen_type = u_unfilled_generator(prim,
start,
count,
hwtnl->api_fillmode,
&gen_prim,
&gen_size, &gen_nr, &gen_func);
}
else {
/* Convert PIPE_PRIM_LINE_LOOP to PIPE_PRIM_LINESTRIP,
* convert PIPE_PRIM_POLYGON to PIPE_PRIM_TRIANGLE_FAN,
* etc, if needed (as determined by svga_hw_prims mask).
*/
gen_type = u_index_generator(svga_hw_prims,
prim,
start,
count,
api_pv,
hwtnl->hw_pv,
&gen_prim, &gen_size, &gen_nr, &gen_func);
}
if (gen_type == U_GENERATE_LINEAR) {
ret = simple_draw_arrays(hwtnl, gen_prim, start, count,
start_instance, instance_count);
}
else {
struct pipe_resource *gen_buf = NULL;
/* Need to draw as indexed primitive.
* Potentially need to run the gen func to build an index buffer.
*/
ret = retrieve_or_generate_indices(hwtnl,
prim,
gen_type,
gen_nr,
gen_size, gen_func, &gen_buf);
if (ret == PIPE_OK) {
pipe_debug_message(&svga->debug.callback, PERF_INFO,
"generating temporary index buffer for drawing %s",
u_prim_name(prim));
ret = svga_hwtnl_simple_draw_range_elements(hwtnl,
gen_buf,
gen_size,
start,
0,
count - 1,
gen_prim, 0, gen_nr,
start_instance,
instance_count);
}
if (gen_buf) {
pipe_resource_reference(&gen_buf, NULL);
}
}
SVGA_STATS_TIME_POP(svga_sws(svga));
return ret;
}