summaryrefslogtreecommitdiff
path: root/dom/canvas/WebGLUniformLocation.cpp
blob: ccd6685b0ec3f2f41c32e591352b9276a524ac51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "WebGLUniformLocation.h"

#include "GLContext.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "WebGLActiveInfo.h"
#include "WebGLContext.h"
#include "WebGLProgram.h"

namespace mozilla {

WebGLUniformLocation::WebGLUniformLocation(WebGLContext* webgl,
                                           const webgl::LinkedProgramInfo* linkInfo,
                                           webgl::UniformInfo* info, GLuint loc,
                                           size_t arrayIndex)
    : WebGLContextBoundObject(webgl)
    , mLinkInfo(linkInfo)
    , mInfo(info)
    , mLoc(loc)
    , mArrayIndex(arrayIndex)
{ }

WebGLUniformLocation::~WebGLUniformLocation()
{ }

bool
WebGLUniformLocation::ValidateForProgram(const WebGLProgram* prog,
                                         const char* funcName) const
{
    // Check the weak-pointer.
    if (!mLinkInfo) {
        mContext->ErrorInvalidOperation("%s: This uniform location is obsolete because"
                                        " its program has been successfully relinked.",
                                        funcName);
        return false;
    }

    if (mLinkInfo->prog != prog) {
        mContext->ErrorInvalidOperation("%s: This uniform location corresponds to a"
                                        " different program.", funcName);
        return false;
    }

    return true;
}

static bool
IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType)
{
    // The order in this switch matches table 2.10 from OpenGL ES
    // 3.0.4 (Aug 27, 2014) es_spec_3.0.4.pdf
    switch (uniformType) {
    case LOCAL_GL_FLOAT:
    case LOCAL_GL_FLOAT_VEC2:
    case LOCAL_GL_FLOAT_VEC3:
    case LOCAL_GL_FLOAT_VEC4:
        return setterType == LOCAL_GL_FLOAT;

    case LOCAL_GL_INT:
    case LOCAL_GL_INT_VEC2:
    case LOCAL_GL_INT_VEC3:
    case LOCAL_GL_INT_VEC4:
        return setterType == LOCAL_GL_INT;

    case LOCAL_GL_UNSIGNED_INT:
    case LOCAL_GL_UNSIGNED_INT_VEC2:
    case LOCAL_GL_UNSIGNED_INT_VEC3:
    case LOCAL_GL_UNSIGNED_INT_VEC4:
        return setterType == LOCAL_GL_UNSIGNED_INT;

        /* bool can be set via any function: 0, 0.0f -> FALSE, _ -> TRUE */
    case LOCAL_GL_BOOL:
    case LOCAL_GL_BOOL_VEC2:
    case LOCAL_GL_BOOL_VEC3:
    case LOCAL_GL_BOOL_VEC4:
        return (setterType == LOCAL_GL_INT   ||
                setterType == LOCAL_GL_FLOAT ||
                setterType == LOCAL_GL_UNSIGNED_INT);

    case LOCAL_GL_FLOAT_MAT2:
    case LOCAL_GL_FLOAT_MAT3:
    case LOCAL_GL_FLOAT_MAT4:
    case LOCAL_GL_FLOAT_MAT2x3:
    case LOCAL_GL_FLOAT_MAT2x4:
    case LOCAL_GL_FLOAT_MAT3x2:
    case LOCAL_GL_FLOAT_MAT3x4:
    case LOCAL_GL_FLOAT_MAT4x2:
    case LOCAL_GL_FLOAT_MAT4x3:
        return setterType == LOCAL_GL_FLOAT;

        /* Samplers can only be set via Uniform1i */
    case LOCAL_GL_SAMPLER_2D:
    case LOCAL_GL_SAMPLER_3D:
    case LOCAL_GL_SAMPLER_CUBE:
    case LOCAL_GL_SAMPLER_2D_SHADOW:
    case LOCAL_GL_SAMPLER_2D_ARRAY:
    case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
    case LOCAL_GL_SAMPLER_CUBE_SHADOW:

    case LOCAL_GL_INT_SAMPLER_2D:
    case LOCAL_GL_INT_SAMPLER_3D:
    case LOCAL_GL_INT_SAMPLER_CUBE:
    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:

    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
        return setterType == LOCAL_GL_INT;

    default:
        MOZ_CRASH("GFX: Bad `uniformType`.");
    }
}

bool
WebGLUniformLocation::ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType,
                                          const char* funcName) const
{
    MOZ_ASSERT(mLinkInfo);

    const auto& uniformElemSize = mInfo->mActiveInfo->mElemSize;
    if (setterElemSize != uniformElemSize) {
        mContext->ErrorInvalidOperation("%s: Function used differs from uniform size: %i",
                                        funcName, uniformElemSize);
        return false;
    }

    const auto& uniformElemType = mInfo->mActiveInfo->mElemType;
    if (!IsUniformSetterTypeValid(setterType, uniformElemType)) {
        mContext->ErrorInvalidOperation("%s: Function used is incompatible with uniform"
                                        " type: %i",
                                        funcName, uniformElemType);
        return false;
    }

    return true;
}

bool
WebGLUniformLocation::ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize,
                                          const char* funcName) const
{
    MOZ_ASSERT(mLinkInfo);

    if (setterArraySize == 0 ||
        setterArraySize % setterElemSize)
    {
        mContext->ErrorInvalidValue("%s: Expected an array of length a multiple of %d,"
                                    " got an array of length %d.",
                                    funcName, setterElemSize, setterArraySize);
        return false;
    }

    /* GLES 2.0.25, Section 2.10, p38
     *   When loading `N` elements starting at an arbitrary position `k` in a uniform
     *   declared as an array, elements `k` through `k + N - 1` in the array will be
     *   replaced with the new values. Values for any array element that exceeds the
     *   highest array element index used, as reported by `GetActiveUniform`, will be
     *   ignored by GL.
     */
    if (!mInfo->mActiveInfo->mIsArray &&
        setterArraySize != setterElemSize)
    {
        mContext->ErrorInvalidOperation("%s: Expected an array of length exactly %d"
                                        " (since this uniform is not an array uniform),"
                                        " got an array of length %d.",
                                        funcName, setterElemSize, setterArraySize);
        return false;
    }

    return true;
}

JS::Value
WebGLUniformLocation::GetUniform(JSContext* js) const
{
    MOZ_ASSERT(mLinkInfo);

    const uint8_t elemSize = mInfo->mActiveInfo->mElemSize;
    static const uint8_t kMaxElemSize = 16;
    MOZ_ASSERT(elemSize <= kMaxElemSize);

    GLuint prog = mLinkInfo->prog->mGLName;

    gl::GLContext* gl = mContext->GL();
    gl->MakeCurrent();

    switch (mInfo->mActiveInfo->mElemType) {
    case LOCAL_GL_INT:
    case LOCAL_GL_INT_VEC2:
    case LOCAL_GL_INT_VEC3:
    case LOCAL_GL_INT_VEC4:
    case LOCAL_GL_SAMPLER_2D:
    case LOCAL_GL_SAMPLER_3D:
    case LOCAL_GL_SAMPLER_CUBE:
    case LOCAL_GL_SAMPLER_2D_SHADOW:
    case LOCAL_GL_SAMPLER_2D_ARRAY:
    case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
    case LOCAL_GL_SAMPLER_CUBE_SHADOW:
    case LOCAL_GL_INT_SAMPLER_2D:
    case LOCAL_GL_INT_SAMPLER_3D:
    case LOCAL_GL_INT_SAMPLER_CUBE:
    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
        {
            GLint buffer[kMaxElemSize] = {0};
            gl->fGetUniformiv(prog, mLoc, buffer);

            if (elemSize == 1)
                return JS::Int32Value(buffer[0]);

            JSObject* obj = dom::Int32Array::Create(js, mContext, elemSize, buffer);
            if (!obj) {
                mContext->ErrorOutOfMemory("getUniform: Out of memory.");
                return JS::NullValue();
            }
            return JS::ObjectOrNullValue(obj);
        }

    case LOCAL_GL_BOOL:
    case LOCAL_GL_BOOL_VEC2:
    case LOCAL_GL_BOOL_VEC3:
    case LOCAL_GL_BOOL_VEC4:
        {
            GLint buffer[kMaxElemSize] = {0};
            gl->fGetUniformiv(prog, mLoc, buffer);

            if (elemSize == 1)
                return JS::BooleanValue(buffer[0]);

            bool boolBuffer[kMaxElemSize];
            for (uint8_t i = 0; i < kMaxElemSize; i++)
                boolBuffer[i] = buffer[i];

            JS::RootedValue val(js);
            // Be careful: we don't want to convert all of |uv|!
            if (!dom::ToJSValue(js, boolBuffer, elemSize, &val)) {
                mContext->ErrorOutOfMemory("getUniform: Out of memory.");
                return JS::NullValue();
            }
            return val;
        }

    case LOCAL_GL_FLOAT:
    case LOCAL_GL_FLOAT_VEC2:
    case LOCAL_GL_FLOAT_VEC3:
    case LOCAL_GL_FLOAT_VEC4:
    case LOCAL_GL_FLOAT_MAT2:
    case LOCAL_GL_FLOAT_MAT3:
    case LOCAL_GL_FLOAT_MAT4:
    case LOCAL_GL_FLOAT_MAT2x3:
    case LOCAL_GL_FLOAT_MAT2x4:
    case LOCAL_GL_FLOAT_MAT3x2:
    case LOCAL_GL_FLOAT_MAT3x4:
    case LOCAL_GL_FLOAT_MAT4x2:
    case LOCAL_GL_FLOAT_MAT4x3:
        {
            GLfloat buffer[16] = {0.0f};
            gl->fGetUniformfv(prog, mLoc, buffer);

            if (elemSize == 1)
                return JS::DoubleValue(buffer[0]);

            JSObject* obj = dom::Float32Array::Create(js, mContext, elemSize, buffer);
            if (!obj) {
                mContext->ErrorOutOfMemory("getUniform: Out of memory.");
                return JS::NullValue();
            }
            return JS::ObjectOrNullValue(obj);
        }

    case LOCAL_GL_UNSIGNED_INT:
    case LOCAL_GL_UNSIGNED_INT_VEC2:
    case LOCAL_GL_UNSIGNED_INT_VEC3:
    case LOCAL_GL_UNSIGNED_INT_VEC4:
        {
            GLuint buffer[kMaxElemSize] = {0};
            gl->fGetUniformuiv(prog, mLoc, buffer);

            if (elemSize == 1)
                return JS::DoubleValue(buffer[0]); // This is Double because only Int32 is special cased.

            JSObject* obj = dom::Uint32Array::Create(js, mContext, elemSize, buffer);
            if (!obj) {
                mContext->ErrorOutOfMemory("getUniform: Out of memory.");
                return JS::NullValue();
            }
            return JS::ObjectOrNullValue(obj);
        }

    default:
        MOZ_CRASH("GFX: Invalid elemType.");
    }
}

////////////////////////////////////////////////////////////////////////////////

JSObject*
WebGLUniformLocation::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
{
    return dom::WebGLUniformLocationBinding::Wrap(js, this, givenProto);
}

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocation)

NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLUniformLocation, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLUniformLocation, Release)

} // namespace mozilla