YaGL: implement type conversion during data transfer
authorIgor Mitsyanko <i.mitsyanko@samsung.com>
Thu, 20 Dec 2012 10:51:39 +0000 (14:51 +0400)
committerStanislav Vorobiov <s.vorobiov@samsung.com>
Mon, 15 Apr 2013 11:24:18 +0000 (15:24 +0400)
In some cases befor transfering data to host openGL, e have to convert it to
another format. This is needed mainly for future GLES1 implementation and it needs
to cover two cases:
convert GL_FIXED data to GL_FLOAT for all transfers
convert GL_BYTE to GL_FLOAT if required (not for all transfers)

Since we cannot know for sure if we need to perform conversion when transfer occurs,
we ned to introduce new member "need_convert" in array state for this.

hw/yagl_apis/gles/yagl_gles_array.c
hw/yagl_apis/gles/yagl_gles_array.h
hw/yagl_apis/gles/yagl_gles_buffer.c
hw/yagl_apis/gles/yagl_gles_buffer.h
hw/yagl_apis/gles/yagl_host_gles_calls.c
hw/yagl_apis/gles2/yagl_gles2_context.c
hw/yagl_apis/gles2/yagl_host_gles2_calls.c

index ea09caf..26e80e5 100644 (file)
@@ -47,6 +47,60 @@ static void yagl_gles_array_reset(struct yagl_gles_array *array)
     }
 }
 
+/*
+ * 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,
@@ -82,6 +136,7 @@ void yagl_gles_array_enable(struct yagl_gles_array *array, bool enable)
 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)
@@ -94,6 +149,7 @@ bool yagl_gles_array_update(struct yagl_gles_array *array,
 
     array->size = size;
     array->type = type;
+    array->need_convert = need_convert;
     array->normalized = normalized;
     array->stride = stride;
 
@@ -103,12 +159,17 @@ bool yagl_gles_array_update(struct yagl_gles_array *array,
 
     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,
@@ -125,6 +186,7 @@ bool yagl_gles_array_update_vbo(struct yagl_gles_array *array,
 
     array->size = size;
     array->type = type;
+    array->need_convert = need_convert;
     array->normalized = normalized;
     array->stride = stride;
 
@@ -136,6 +198,10 @@ bool yagl_gles_array_update_vbo(struct yagl_gles_array *array,
     array->vbo_local_name = vbo_local_name;
     array->offset = offset;
 
+    if (need_convert) {
+        assert(type == GL_FIXED || type == GL_BYTE);
+    }
+
     return true;
 }
 
@@ -150,9 +216,10 @@ bool yagl_gles_array_transfer(struct yagl_gles_array *array,
     }
 
     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;
@@ -180,18 +247,25 @@ bool yagl_gles_array_transfer(struct yagl_gles_array *array,
             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;
 }
index 75249ec..abe6591 100644 (file)
@@ -45,6 +45,16 @@ struct yagl_gles_array
     GLsizei stride;
 
     /*
+     * Specifies if array data needs to be converted before transfering to
+     * host OpenGL occures.
+     * This could be used with GL_FIXED or GL_BYTE types, then data is
+     * converted to GL_FLOAT or GL_SHORT respectively. Setting this flag
+     * when yagl_gles_array::type is not either GL_FIXED or GL_FLOAT is
+     * programming error.
+     */
+    bool need_convert;
+
+    /*
      * Is array enabled by 'glEnableClientState'/'glEnableVertexAttribArray'.
      */
     bool enabled;
@@ -88,6 +98,7 @@ void yagl_gles_array_enable(struct yagl_gles_array *array, bool enable);
 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);
@@ -95,6 +106,7 @@ bool yagl_gles_array_update(struct yagl_gles_array *array,
 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,
index d1b340c..094bbe3 100644 (file)
@@ -1,4 +1,4 @@
-#include <GL/gl.h>
+#include <GLES/gl.h>
 #include "yagl_gles_buffer.h"
 #include "yagl_gles_driver.h"
 #include "yagl_gles_validate.h"
@@ -22,24 +22,65 @@ static void yagl_gles_buffer_transfer_default(struct yagl_gles_buffer *buffer,
     }
 }
 
+/*
+ * 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,
@@ -97,10 +138,12 @@ static void yagl_gles_buffer_destroy(struct yagl_ref *ref)
     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);
 
@@ -130,6 +173,9 @@ struct yagl_gles_buffer
     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;
 }
 
@@ -173,9 +219,11 @@ void yagl_gles_buffer_set_data(struct yagl_gles_buffer *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);
 }
@@ -201,6 +249,7 @@ bool yagl_gles_buffer_update_data(struct yagl_gles_buffer *buffer,
 
     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);
 
@@ -264,55 +313,70 @@ bool yagl_gles_buffer_get_minmax_index(struct yagl_gles_buffer *buffer,
 
 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,
index 0408815..c4291b6 100644 (file)
@@ -6,21 +6,21 @@
 #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
 
@@ -43,6 +43,7 @@ struct yagl_gles_buffer
 
     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;
@@ -83,12 +84,14 @@ bool yagl_gles_buffer_get_minmax_index(struct yagl_gles_buffer *buffer,
 
 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,
index fd874c1..aaeab6b 100644 (file)
@@ -718,9 +718,13 @@ bool yagl_host_glDrawElements(GLenum mode,
         }
         yagl_gles_buffer_bind(ctx->ebo,
                               type,
+                              false,
                               GL_ELEMENT_ARRAY_BUFFER,
                               &old_buffer_name);
-        yagl_gles_buffer_transfer(ctx->ebo, type, GL_ELEMENT_ARRAY_BUFFER);
+        yagl_gles_buffer_transfer(ctx->ebo,
+                                  type,
+                                  GL_ELEMENT_ARRAY_BUFFER,
+                                  false);
         ebo_bound = true;
     } else {
         if (!yagl_gles_get_index_size(type, &index_size)) {
index 6f7a35f..908983a 100644 (file)
@@ -24,6 +24,7 @@ static void yagl_gles2_array_apply(struct yagl_gles_array *array)
 
         yagl_gles_buffer_bind(array->vbo,
                               array->type,
+                              false,
                               GL_ARRAY_BUFFER,
                               &old_buffer_name);
 
index 3354efb..4243e2a 100644 (file)
@@ -2704,6 +2704,7 @@ bool yagl_host_glVertexAttribPointer(GLuint indx,
         if (!yagl_gles_array_update_vbo(array,
                                         size,
                                         type,
+                                        false,
                                         normalized,
                                         stride,
                                         ctx->base.vbo,
@@ -2715,6 +2716,7 @@ bool yagl_host_glVertexAttribPointer(GLuint indx,
         if (!yagl_gles_array_update(array,
                                     size,
                                     type,
+                                    false,
                                     normalized,
                                     stride,
                                     ptr)) {