328 lines
10 KiB
C
328 lines
10 KiB
C
/*
|
|
* 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, const 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,
|
|
const struct pipe_draw_indirect_info *indirect_info,
|
|
const struct pipe_draw_start_count *draw,
|
|
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 = draw->count;
|
|
unsigned start = draw->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 (indirect_info && indirect_info->buffer) {
|
|
indirect = read_indirect_elements(context, indirect_info);
|
|
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,
|
|
draw->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 struct pipe_draw_indirect_info *indirect_info,
|
|
const struct pipe_draw_start_count *draw)
|
|
{
|
|
const void *src_map;
|
|
struct range_info ranges = {0};
|
|
struct pipe_draw_info new_info;
|
|
struct pipe_draw_start_count new_draw;
|
|
struct pipe_transfer *src_transfer = NULL;
|
|
unsigned i, start, count;
|
|
DrawElementsIndirectCommand indirect;
|
|
unsigned info_start = draw->start;
|
|
unsigned info_count = draw->count;
|
|
unsigned info_instance_count = info->instance_count;
|
|
|
|
assert(info->index_size);
|
|
assert(info->primitive_restart);
|
|
|
|
if (indirect_info && indirect_info->buffer) {
|
|
indirect = read_indirect_elements(context, indirect_info);
|
|
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;
|
|
new_draw = *draw;
|
|
/* we've effectively remapped this to a direct draw */
|
|
new_info.instance_count = info_instance_count;
|
|
new_info.primitive_restart = FALSE;
|
|
for (i = 0; i < ranges.count; i++) {
|
|
new_draw.start = ranges.ranges[i].start;
|
|
new_draw.count = ranges.ranges[i].count;
|
|
context->draw_vbo(context, &new_info, NULL, &new_draw, 1);
|
|
}
|
|
|
|
FREE(ranges.ranges);
|
|
|
|
return PIPE_OK;
|
|
}
|