}
}
+/*
+ * GLES1 could use GL_FIXED data type, which is not supported by host OpenGL.
+ * Also, GLES1 glTexCoordPointer and glVertexPointer could use GL_BYTE type
+ * for data while host OpenGL doesn't support this type for these functions.
+ * Conversion to host-acceptable data type is required for either of these
+ * cases.
+ * We only change host_data and host_data_size here, because other
+ * array parameters are guest visible.
+ */
+
+/* After conversion from GL_BYTE to GL_SHORT host_data is always tightly
+ * packed (stride = 0) */
+static void yagl_gles1_array_byte_to_short(struct yagl_gles_array *array,
+ uint32_t first,
+ uint32_t count)
+{
+ uint32_t new_data_size;
+ GLshort *new_data;
+ GLbyte *old_data = array->host_data + first * array->stride;
+ unsigned i, j;
+
+ new_data_size = (first + count) * array->size * sizeof(GLshort);
+ new_data = g_malloc(new_data_size);
+
+ for (i = first * array->size; i < (first + count) * array->size; i += array->size) {
+ for (j = 0; j < array->size; ++j) {
+ new_data[i + j] = old_data[j];
+ }
+ old_data += array->stride;
+ }
+
+ g_free(array->host_data);
+ array->host_data = new_data;
+ array->host_data_size = new_data_size;
+}
+
+static void yagl_gles1_array_fixed_to_float(struct yagl_gles_array *array,
+ uint32_t first,
+ uint32_t count)
+{
+ uint32_t offset = first * array->stride;
+ void *data;
+ unsigned i;
+
+ for (; offset < (first + count) * array->stride; offset += array->stride) {
+ data = array->host_data + offset;
+
+ for (i = 0; i < array->size; ++i) {
+ ((GLfloat *)data)[i] = yagl_fixed_to_float(((GLfixed *)data)[i]);
+ }
+ }
+}
+
+
void yagl_gles_array_init(struct yagl_gles_array *array,
GLuint index,
struct yagl_gles_context *ctx,
bool yagl_gles_array_update(struct yagl_gles_array *array,
GLint size,
GLenum type,
+ bool need_convert,
GLboolean normalized,
GLsizei stride,
target_ulong target_data)
array->size = size;
array->type = type;
+ array->need_convert = need_convert;
array->normalized = normalized;
array->stride = stride;
array->target_data = target_data;
+ if (need_convert) {
+ assert(type == GL_FIXED || type == GL_BYTE);
+ }
+
return true;
}
bool yagl_gles_array_update_vbo(struct yagl_gles_array *array,
GLint size,
GLenum type,
+ bool need_convert,
GLboolean normalized,
GLsizei stride,
struct yagl_gles_buffer *vbo,
array->size = size;
array->type = type;
+ array->need_convert = need_convert;
array->normalized = normalized;
array->stride = stride;
array->vbo_local_name = vbo_local_name;
array->offset = offset;
+ if (need_convert) {
+ assert(type == GL_FIXED || type == GL_BYTE);
+ }
+
return true;
}
}
if (array->vbo) {
- yagl_gles_buffer_transfer(array->vbo, array->type, GL_ARRAY_BUFFER);
-
- array->apply(array);
+ yagl_gles_buffer_transfer(array->vbo,
+ array->type,
+ GL_ARRAY_BUFFER,
+ array->need_convert);
} else {
if (!array->target_data) {
return true;
array->host_data = g_malloc(array->host_data_size);
}
- /*
- * TODO: fix for GL_FIXED case.
- */
-
if (!yagl_mem_get(array->target_data + (first * array->stride),
(count * array->stride),
array->host_data + (first * array->stride))) {
return false;
}
- array->apply(array);
+ if (array->need_convert) {
+ switch (array->type) {
+ case GL_BYTE:
+ yagl_gles1_array_byte_to_short(array, first, count);
+ break;
+ case GL_FIXED:
+ yagl_gles1_array_fixed_to_float(array, first, count);
+ break;
+ }
+ }
}
+ array->apply(array);
+
return true;
}
-#include <GL/gl.h>
+#include <GLES/gl.h>
#include "yagl_gles_buffer.h"
#include "yagl_gles_driver.h"
#include "yagl_gles_validate.h"
}
}
+/*
+ * Here we directly substitute target GLfixed data with GLfloat data.
+ * We can do that because GLfloat and GLfixed have the same data length
+ * and because buffer data is not directly visible to guest system.
+ * In the future, if something like glMapBuffer would have to be implemented,
+ * this approach should be changed to allocating intermediate buffer for
+ * passing to glBuferData call. The only two users who access buffer data
+ * directly is yagl_gles1_draw_arrays_psize/yagl_gles1_draw_elem_psize,
+ * they would have to be modified to perform manual conversion from GL_FIXED
+ * to GL_FLOAT, or to use glMapBuffer call themselves.
+ */
+
static void yagl_gles_buffer_transfer_fixed(struct yagl_gles_buffer *buffer,
GLenum target,
int start,
int size)
{
- /*
- * TODO: Convert to GL_FLOAT
- */
+ void *data = buffer->data + start;;
+ int i;
+
+ assert(sizeof(GLfixed) == sizeof(GLfloat));
+
+ for (i = 0; i < (size / sizeof(GLfloat)); ++i) {
+ ((GLfloat *)data)[i] = yagl_fixed_to_float(((GLfixed *)data)[i]);
+ }
if ((start == 0) && (size == buffer->size)) {
- buffer->driver->BufferData(target, size,
- buffer->data, buffer->usage);
+ buffer->driver->BufferData(target, size, data, buffer->usage);
} else {
- buffer->driver->BufferSubData(target, start,
- size, buffer->data + start);
+ buffer->driver->BufferSubData(target, start, size, data);
}
}
+static void yagl_gles_buffer_transfer_byte(struct yagl_gles_buffer *buffer,
+ GLenum target,
+ int start,
+ int size)
+{
+ int host_size = size * sizeof(GLshort);
+ GLbyte *target_data = buffer->data + start;
+ GLshort *host_data;
+ int i;
+
+ host_data = g_malloc(host_size);
+
+ for (i = 0; i < size; ++i) {
+ host_data[i] = target_data[i];
+ }
+
+ if ((start == 0) && (size == buffer->size)) {
+ buffer->driver->BufferData(target, host_size, host_data, buffer->usage);
+ } else {
+ buffer->driver->BufferSubData(target, start * sizeof(GLshort),
+ host_size, host_data);
+ }
+
+ g_free(host_data);
+}
+
static void yagl_gles_buffer_transfer_internal(struct yagl_gles_buffer *buffer,
struct yagl_range_list *range_list,
GLenum target,
yagl_ensure_ctx();
buffer->driver->DeleteBuffers(1, &buffer->default_part.global_name);
buffer->driver->DeleteBuffers(1, &buffer->fixed_part.global_name);
+ buffer->driver->DeleteBuffers(1, &buffer->byte_part.global_name);
yagl_unensure_ctx();
yagl_range_list_cleanup(&buffer->default_part.range_list);
yagl_range_list_cleanup(&buffer->fixed_part.range_list);
+ yagl_range_list_cleanup(&buffer->byte_part.range_list);
qemu_mutex_destroy(&buffer->mutex);
driver->GenBuffers(1, &buffer->fixed_part.global_name);
yagl_range_list_init(&buffer->fixed_part.range_list);
+ driver->GenBuffers(1, &buffer->byte_part.global_name);
+ yagl_range_list_init(&buffer->byte_part.range_list);
+
return buffer;
}
yagl_range_list_clear(&buffer->default_part.range_list);
yagl_range_list_clear(&buffer->fixed_part.range_list);
+ yagl_range_list_clear(&buffer->byte_part.range_list);
yagl_range_list_add(&buffer->default_part.range_list, 0, buffer->size);
yagl_range_list_add(&buffer->fixed_part.range_list, 0, buffer->size);
+ yagl_range_list_add(&buffer->byte_part.range_list, 0, buffer->size);
qemu_mutex_unlock(&buffer->mutex);
}
yagl_range_list_add(&buffer->default_part.range_list, offset, size);
yagl_range_list_add(&buffer->fixed_part.range_list, offset, size);
+ yagl_range_list_add(&buffer->byte_part.range_list, offset, size);
qemu_mutex_unlock(&buffer->mutex);
bool yagl_gles_buffer_bind(struct yagl_gles_buffer *buffer,
GLenum type,
+ bool need_convert,
GLenum target,
yagl_object_name *old_buffer_name)
{
GLenum binding;
yagl_object_name tmp;
+ struct yagl_gles_buffer_part *bufpart = &buffer->default_part;
if (!yagl_gles_buffer_target_to_binding(target, &binding)) {
return false;
}
- buffer->driver->GetIntegerv(binding,
- (GLint*)&tmp);
+ buffer->driver->GetIntegerv(binding, (GLint*)&tmp);
if (old_buffer_name) {
*old_buffer_name = tmp;
}
- if (type == GL_FIXED) {
- if (tmp != buffer->fixed_part.global_name) {
- buffer->driver->BindBuffer(target,
- buffer->fixed_part.global_name);
- }
- } else {
- if (tmp != buffer->default_part.global_name) {
- buffer->driver->BindBuffer(target,
- buffer->default_part.global_name);
+ if (need_convert) {
+ switch (type) {
+ case GL_BYTE:
+ bufpart = &buffer->byte_part;
+ break;
+ case GL_FIXED:
+ bufpart = &buffer->fixed_part;
+ break;
}
}
+ if (tmp != bufpart->global_name) {
+ buffer->driver->BindBuffer(target, bufpart->global_name);
+ }
+
return true;
}
bool yagl_gles_buffer_transfer(struct yagl_gles_buffer *buffer,
GLenum type,
- GLenum target)
+ GLenum target,
+ bool need_convert)
{
yagl_object_name old_buffer_name = 0;
- if (!yagl_gles_buffer_bind(buffer, type, target, &old_buffer_name)) {
+ if (!yagl_gles_buffer_bind(buffer, type, need_convert, target, &old_buffer_name)) {
return false;
}
qemu_mutex_lock(&buffer->mutex);
- if (type == GL_FIXED) {
- yagl_gles_buffer_transfer_internal(buffer,
- &buffer->fixed_part.range_list,
- target,
- &yagl_gles_buffer_transfer_fixed);
+ if (need_convert) {
+ switch (type) {
+ case GL_BYTE:
+ yagl_gles_buffer_transfer_internal(buffer,
+ &buffer->byte_part.range_list,
+ target,
+ &yagl_gles_buffer_transfer_byte);
+ break;
+ case GL_FIXED:
+ yagl_gles_buffer_transfer_internal(buffer,
+ &buffer->fixed_part.range_list,
+ target,
+ &yagl_gles_buffer_transfer_fixed);
+ break;
+ }
} else {
yagl_gles_buffer_transfer_internal(buffer,
&buffer->default_part.range_list,
#include "yagl_range_list.h"
#include "qemu-thread.h"
-/*
- * VBO implementation is somewhat tricky because
- * we must correctly handle GL_FIXED data type, which must be
- * converted to GL_FLOAT. Buffer objects may be bound to
- * multiple contexts, so there may be several VBOs using the
- * same buffer object, but having different data types, among which
- * are GL_FIXED and GL_FLOAT. We handle this by having
- * two 'yagl_gles_buffer_part' objects: one for GL_FIXED type and
- * the other for all other types. Each 'yagl_gles_buffer_part' object
- * has a separate global buffer name and a range list that consists of
- * 'glBufferData' and 'glBufferSubData' ranges which haven't been processed
- * yet. When a VBO is used we walk the appropriate range list, make
- * conversions, clear it and call 'glBufferData' or
- * 'glBufferSubData' as many times as needed.
- */
+ /*
+ * VBO implementation is somewhat tricky because
+ * we must correctly handle GL_FIXED data type, which must be
+ * converted to GL_FLOAT, and GL_BYTE data type, which (in some cases)
+ * must be converted to GL_SHORT. Buffer objects may be bound to
+ * multiple contexts, so there may be several VBOs using the
+ * same buffer object, but having different data types. We handle this
+ * by having three 'yagl_gles_buffer_part' objects: one for GL_FIXED type,
+ * one for GL_BYTE type and the other for all other types.
+ * Each 'yagl_gles_buffer_part' object has a separate global buffer name
+ * and a range list that consists of 'glBufferData' and 'glBufferSubData'
+ * ranges which haven't been processed yet. When a VBO is used we walk
+ * the appropriate range list, make conversions, clear it and call
+ * 'glBufferData' or 'glBufferSubData' as many times as needed.
+ */
#define YAGL_NS_BUFFER 0
struct yagl_gles_buffer_part default_part;
struct yagl_gles_buffer_part fixed_part;
+ struct yagl_gles_buffer_part byte_part;
GLint size;
void *data;
bool yagl_gles_buffer_bind(struct yagl_gles_buffer *buffer,
GLenum type,
+ bool need_convert,
GLenum target,
yagl_object_name *old_buffer_name);
bool yagl_gles_buffer_transfer(struct yagl_gles_buffer *buffer,
GLenum type,
- GLenum target);
+ GLenum target,
+ bool need_convert);
bool yagl_gles_buffer_get_parameter(struct yagl_gles_buffer *buffer,
GLenum pname,