* 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 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.
+ * 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 "bufferobj.h"
#include "context.h"
#include "hash.h"
-#include "mfeatures.h"
+#include "macros.h"
#include "mtypes.h"
#include "transformfeedback.h"
#include "shaderapi.h"
}
else {
obj->RefCount++;
+ obj->EverBound = GL_TRUE;
*ptr = obj;
}
}
if (obj) {
obj->Name = name;
obj->RefCount = 1;
+ obj->EverBound = GL_FALSE;
}
return obj;
}
}
+/**
+ * Fill in the correct Size value for each buffer in \c obj.
+ *
+ * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed
+ * Targets"):
+ *
+ * BindBufferBase binds the entire buffer, even when the size of the buffer
+ * is changed after the binding is established. It is equivalent to calling
+ * BindBufferRange with offset zero, while size is determined by the size of
+ * the bound buffer at the time the binding is used.
+ *
+ * Regardless of the size specified with BindBufferRange, or indirectly with
+ * BindBufferBase, the GL will never read or write beyond the end of a bound
+ * buffer. In some cases this constraint may result in visibly different
+ * behavior when a buffer overflow would otherwise result, such as described
+ * for transform feedback operations in section 13.2.2.
+ */
+static void
+compute_transform_feedback_buffer_sizes(
+ struct gl_transform_feedback_object *obj)
+{
+ unsigned i = 0;
+ for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) {
+ GLintptr offset = obj->Offset[i];
+ GLsizeiptr buffer_size
+ = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size;
+ GLsizeiptr available_space
+ = buffer_size <= offset ? 0 : buffer_size - offset;
+ GLsizeiptr computed_size;
+ if (obj->RequestedSize[i] == 0) {
+ /* No size was specified at the time the buffer was bound, so allow
+ * writing to all available space in the buffer.
+ */
+ computed_size = available_space;
+ } else {
+ /* A size was specified at the time the buffer was bound, however
+ * it's possible that the buffer has shrunk since then. So only
+ * allow writing to the minimum of the specified size and the space
+ * available.
+ */
+ computed_size = MIN2(available_space, obj->RequestedSize[i]);
+ }
+
+ /* Legal sizes must be multiples of four, so round down if necessary. */
+ obj->Size[i] = computed_size & ~0x3;
+ }
+}
+
+
+/**
+ * Compute the maximum number of vertices that can be written to the currently
+ * enabled transform feedback buffers without overflowing any of them.
+ */
+unsigned
+_mesa_compute_max_transform_feedback_vertices(
+ const struct gl_transform_feedback_object *obj,
+ const struct gl_transform_feedback_info *info)
+{
+ unsigned max_index = 0xffffffff;
+ unsigned i;
+
+ for (i = 0; i < info->NumBuffers; ++i) {
+ unsigned stride = info->BufferStride[i];
+ unsigned max_for_this_buffer;
+
+ /* Skip any inactive buffers, which have a stride of 0. */
+ if (stride == 0)
+ continue;
+
+ max_for_this_buffer = obj->Size[i] / (4 * stride);
+ max_index = MIN2(max_index, max_for_this_buffer);
+ }
+
+ return max_index;
+}
+
+
/**
** Begin API functions
**/
struct gl_transform_feedback_object *obj;
struct gl_transform_feedback_info *info;
GLuint i;
+ unsigned vertices_per_prim;
GET_CURRENT_CONTEXT(ctx);
obj = ctx->TransformFeedback.CurrentObject;
switch (mode) {
case GL_POINTS:
+ vertices_per_prim = 1;
+ break;
case GL_LINES:
+ vertices_per_prim = 2;
+ break;
case GL_TRIANGLES:
- /* legal */
+ vertices_per_prim = 3;
break;
default:
_mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
}
}
- FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
+ FLUSH_VERTICES(ctx, 0);
+ ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
+
obj->Active = GL_TRUE;
ctx->TransformFeedback.Mode = mode;
+ compute_transform_feedback_buffer_sizes(obj);
+
+ if (_mesa_is_gles3(ctx)) {
+ /* In GLES3, we are required to track the usage of the transform
+ * feedback buffer and report INVALID_OPERATION if a draw call tries to
+ * exceed it. So compute the maximum number of vertices that we can
+ * write without overflowing any of the buffers currently being used for
+ * feedback.
+ */
+ unsigned max_vertices
+ = _mesa_compute_max_transform_feedback_vertices(obj, info);
+ obj->GlesRemainingPrims = max_vertices / vertices_per_prim;
+ }
+
assert(ctx->Driver.BeginTransformFeedback);
ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
}
return;
}
- FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
+ FLUSH_VERTICES(ctx, 0);
+ ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
+
ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
struct gl_transform_feedback_object *obj =
ctx->TransformFeedback.CurrentObject;
- /* Note: no need to FLUSH_VERTICES or flag _NEW_TRANSFORM_FEEDBACK, because
+ /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because
* transform feedback buffers can't be changed while transform feedback is
* active.
*/
obj->BufferNames[index] = bufObj->Name;
obj->Offset[index] = offset;
- obj->Size[index] = size;
+ obj->RequestedSize[index] = size;
}
struct gl_buffer_object *bufObj)
{
struct gl_transform_feedback_object *obj;
- GLsizeiptr size;
obj = ctx->TransformFeedback.CurrentObject;
return;
}
- /* default size is the buffer size rounded down to nearest
- * multiple of four.
- */
- size = bufObj->Size & ~0x3;
-
- bind_buffer_range(ctx, index, bufObj, 0, size);
+ bind_buffer_range(ctx, index, bufObj, 0, 0);
}
struct gl_transform_feedback_object *obj;
struct gl_buffer_object *bufObj;
GET_CURRENT_CONTEXT(ctx);
- GLsizeiptr size;
if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
_mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
return;
}
- /* default size is the buffer size rounded down to nearest
- * multiple of four.
- */
- size = (bufObj->Size - offset) & ~0x3;
-
- bind_buffer_range(ctx, index, bufObj, offset, size);
+ bind_buffer_range(ctx, index, bufObj, offset, 0);
}
shProg->TransformFeedback.BufferMode = bufferMode;
- /* No need to set _NEW_TRANSFORM_FEEDBACK (or invoke FLUSH_VERTICES) since
+ /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since
* the varyings won't be used until shader link time.
*/
}
shProg = _mesa_lookup_shader_program(ctx, program);
if (!shProg) {
_mesa_error(ctx, GL_INVALID_VALUE,
- "glGetTransformFeedbackVaryings(program=%u)", program);
+ "glGetTransformFeedbackVarying(program=%u)", program);
return;
}
linked_xfb_info = &shProg->LinkedTransformFeedback;
if (index >= (GLuint) linked_xfb_info->NumVarying) {
_mesa_error(ctx, GL_INVALID_VALUE,
- "glGetTransformFeedbackVaryings(index=%u)", index);
+ "glGetTransformFeedbackVarying(index=%u)", index);
return;
}
GLuint first;
GET_CURRENT_CONTEXT(ctx);
- ASSERT_OUTSIDE_BEGIN_END(ctx);
-
if (n < 0) {
_mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)");
return;
GLboolean GLAPIENTRY
_mesa_IsTransformFeedback(GLuint name)
{
+ struct gl_transform_feedback_object *obj;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
- if (name && _mesa_lookup_transform_feedback_object(ctx, name))
- return GL_TRUE;
- else
+ if (name == 0)
+ return GL_FALSE;
+
+ obj = _mesa_lookup_transform_feedback_object(ctx, name);
+ if (obj == NULL)
return GL_FALSE;
+
+ return obj->EverBound;
}
return;
}
- if (ctx->TransformFeedback.CurrentObject->Active &&
- !ctx->TransformFeedback.CurrentObject->Paused) {
+ if (_mesa_is_xfb_active_and_unpaused(ctx)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glBindTransformFeedback(transform is active, or not paused)");
return;
GLint i;
GET_CURRENT_CONTEXT(ctx);
- ASSERT_OUTSIDE_BEGIN_END(ctx);
-
if (n < 0) {
_mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
return;
obj = ctx->TransformFeedback.CurrentObject;
- if (!obj->Active || obj->Paused) {
+ if (!_mesa_is_xfb_active_and_unpaused(ctx)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glPauseTransformFeedback(feedback not active or already paused)");
return;
}
- FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
+ FLUSH_VERTICES(ctx, 0);
+ ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
+
obj->Paused = GL_TRUE;
assert(ctx->Driver.PauseTransformFeedback);
return;
}
- FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
+ FLUSH_VERTICES(ctx, 0);
+ ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
+
obj->Paused = GL_FALSE;
assert(ctx->Driver.ResumeTransformFeedback);