blob: eef2b4c622cb00d1c82d3878c0e1fe03bb998e8d [file] [log] [blame]
/*
* Copyright 2014 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, sub license, 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 (including the
* next paragraph) 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 NON-INFRINGEMENT.
* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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 "u_inlines.h"
#include "util/u_memory.h"
#include "u_prim_restart.h"
typedef struct {
uint32_t count;
uint32_t primCount;
uint32_t firstIndex;
int32_t baseVertex;
uint32_t reservedMustBeZero;
} DrawElementsIndirectCommand;
static DrawElementsIndirectCommand
read_indirect_elements(struct pipe_context *context, struct pipe_draw_indirect_info *indirect)
{
DrawElementsIndirectCommand ret;
struct pipe_transfer *transfer = NULL;
void *map = NULL;
/* we only need the first 3 members */
unsigned read_size = 3 * sizeof(uint32_t);
assert(indirect->buffer->width0 > 3 * sizeof(uint32_t));
map = pipe_buffer_map_range(context, indirect->buffer,
indirect->offset,
read_size,
PIPE_MAP_READ,
&transfer);
assert(map);
memcpy(&ret, map, read_size);
pipe_buffer_unmap(context, transfer);
return ret;
}
void
util_translate_prim_restart_data(unsigned index_size,
void *src_map, void *dst_map,
unsigned count, unsigned restart_index)
{
if (index_size == 1) {
uint8_t *src = (uint8_t *) src_map;
uint16_t *dst = (uint16_t *) dst_map;
unsigned i;
for (i = 0; i < count; i++) {
dst[i] = (src[i] == restart_index) ? 0xffff : src[i];
}
}
else if (index_size == 2) {
uint16_t *src = (uint16_t *) src_map;
uint16_t *dst = (uint16_t *) dst_map;
unsigned i;
for (i = 0; i < count; i++) {
dst[i] = (src[i] == restart_index) ? 0xffff : src[i];
}
}
else {
uint32_t *src = (uint32_t *) src_map;
uint32_t *dst = (uint32_t *) dst_map;
unsigned i;
assert(index_size == 4);
for (i = 0; i < count; i++) {
dst[i] = (src[i] == restart_index) ? 0xffffffff : src[i];
}
}
}
/**
* Translate an index buffer for primitive restart.
* Create a new index buffer which is a copy of the original index buffer
* except that instances of 'restart_index' are converted to 0xffff or
* 0xffffffff.
* Also, index buffers using 1-byte indexes are converted to 2-byte indexes.
*/
enum pipe_error
util_translate_prim_restart_ib(struct pipe_context *context,
const struct pipe_draw_info *info,
struct pipe_resource **dst_buffer)
{
struct pipe_screen *screen = context->screen;
struct pipe_transfer *src_transfer = NULL, *dst_transfer = NULL;
void *src_map = NULL, *dst_map = NULL;
const unsigned src_index_size = info->index_size;
unsigned dst_index_size;
DrawElementsIndirectCommand indirect;
unsigned count = info->count;
unsigned start = info->start;
/* 1-byte indexes are converted to 2-byte indexes, 4-byte stays 4-byte */
dst_index_size = MAX2(2, info->index_size);
assert(dst_index_size == 2 || dst_index_size == 4);
if (info->indirect) {
indirect = read_indirect_elements(context, info->indirect);
count = indirect.count;
start = indirect.firstIndex;
}
/* Create new index buffer */
*dst_buffer = pipe_buffer_create(screen, PIPE_BIND_INDEX_BUFFER,
PIPE_USAGE_STREAM,
count * dst_index_size);
if (!*dst_buffer)
goto error;
/* Map new / dest index buffer */
dst_map = pipe_buffer_map(context, *dst_buffer,
PIPE_MAP_WRITE, &dst_transfer);
if (!dst_map)
goto error;
if (info->has_user_indices)
src_map = (unsigned char*)info->index.user + start * src_index_size;
else
/* Map original / src index buffer */
src_map = pipe_buffer_map_range(context, info->index.resource,
start * src_index_size,
count * src_index_size,
PIPE_MAP_READ,
&src_transfer);
if (!src_map)
goto error;
util_translate_prim_restart_data(src_index_size, src_map, dst_map,
info->count, info->restart_index);
if (src_transfer)
pipe_buffer_unmap(context, src_transfer);
pipe_buffer_unmap(context, dst_transfer);
return PIPE_OK;
error:
if (src_transfer)
pipe_buffer_unmap(context, src_transfer);
if (dst_transfer)
pipe_buffer_unmap(context, dst_transfer);
if (*dst_buffer)
pipe_resource_reference(dst_buffer, NULL);
return PIPE_ERROR_OUT_OF_MEMORY;
}
/** Helper structs for util_draw_vbo_without_prim_restart() */
struct range {
unsigned start, count;
};
struct range_info {
struct range *ranges;
unsigned count, max;
};
/**
* Helper function for util_draw_vbo_without_prim_restart()
* \return true for success, false if out of memory
*/
static boolean
add_range(struct range_info *info, unsigned start, unsigned count)
{
if (info->max == 0) {
info->max = 10;
info->ranges = MALLOC(info->max * sizeof(struct range));
if (!info->ranges) {
return FALSE;
}
}
else if (info->count == info->max) {
/* grow the ranges[] array */
info->ranges = REALLOC(info->ranges,
info->max * sizeof(struct range),
2 * info->max * sizeof(struct range));
if (!info->ranges) {
return FALSE;
}
info->max *= 2;
}
/* save the range */
info->ranges[info->count].start = start;
info->ranges[info->count].count = count;
info->count++;
return TRUE;
}
/**
* Implement primitive restart by breaking an indexed primitive into
* pieces which do not contain restart indexes. Each piece is then
* drawn by calling pipe_context::draw_vbo().
* \return PIPE_OK if no error, an error code otherwise.
*/
enum pipe_error
util_draw_vbo_without_prim_restart(struct pipe_context *context,
const struct pipe_draw_info *info)
{
const void *src_map;
struct range_info ranges = {0};
struct pipe_draw_info new_info;
struct pipe_transfer *src_transfer = NULL;
unsigned i, start, count;
DrawElementsIndirectCommand indirect;
unsigned info_start = info->start;
unsigned info_count = info->count;
unsigned info_instance_count = info->instance_count;
assert(info->index_size);
assert(info->primitive_restart);
if (info->indirect) {
indirect = read_indirect_elements(context, info->indirect);
info_count = indirect.count;
info_start = indirect.firstIndex;
info_instance_count = indirect.primCount;
}
/* Get pointer to the index data */
if (!info->has_user_indices) {
/* map the index buffer (only the range we need to scan) */
src_map = pipe_buffer_map_range(context, info->index.resource,
info_start * info->index_size,
info_count * info->index_size,
PIPE_MAP_READ,
&src_transfer);
if (!src_map) {
return PIPE_ERROR_OUT_OF_MEMORY;
}
}
else {
if (!info->index.user) {
debug_printf("User-space index buffer is null!");
return PIPE_ERROR_BAD_INPUT;
}
src_map = (const uint8_t *) info->index.user
+ info_start * info->index_size;
}
#define SCAN_INDEXES(TYPE) \
for (i = 0; i <= info_count; i++) { \
if (i == info_count || \
((const TYPE *) src_map)[i] == info->restart_index) { \
/* cut / restart */ \
if (count > 0) { \
if (!add_range(&ranges, info_start + start, count)) { \
if (src_transfer) \
pipe_buffer_unmap(context, src_transfer); \
return PIPE_ERROR_OUT_OF_MEMORY; \
} \
} \
start = i + 1; \
count = 0; \
} \
else { \
count++; \
} \
}
start = 0;
count = 0;
switch (info->index_size) {
case 1:
SCAN_INDEXES(uint8_t);
break;
case 2:
SCAN_INDEXES(uint16_t);
break;
case 4:
SCAN_INDEXES(uint32_t);
break;
default:
assert(!"Bad index size");
return PIPE_ERROR_BAD_INPUT;
}
/* unmap index buffer */
if (src_transfer)
pipe_buffer_unmap(context, src_transfer);
/* draw ranges between the restart indexes */
new_info = *info;
/* we've effectively remapped this to a direct draw */
new_info.indirect = NULL;
new_info.instance_count = info_instance_count;
new_info.primitive_restart = FALSE;
for (i = 0; i < ranges.count; i++) {
new_info.start = ranges.ranges[i].start;
new_info.count = ranges.ranges[i].count;
context->draw_vbo(context, &new_info);
}
FREE(ranges.ranges);
return PIPE_OK;
}