+void ensure_texture_resolution(PBOFrameAllocator::Userdata *userdata, unsigned field, unsigned width, unsigned height, unsigned v210_width)
+{
+ bool first;
+ switch (userdata->pixel_format) {
+ case bmusb::PixelFormat_10BitYCbCr:
+ first = userdata->tex_v210[field] == 0 || userdata->tex_444[field] == 0;
+ break;
+ case bmusb::PixelFormat_8BitYCbCr:
+ first = userdata->tex_y[field] == 0 || userdata->tex_cbcr[field] == 0;
+ break;
+ case bmusb::PixelFormat_8BitBGRA:
+ first = userdata->tex_rgba[field] == 0;
+ break;
+ default:
+ assert(false);
+ }
+
+ if (first ||
+ width != userdata->last_width[field] ||
+ height != userdata->last_height[field]) {
+ // We changed resolution since last use of this texture, so we need to create
+ // a new object. Note that this each card has its own PBOFrameAllocator,
+ // we don't need to worry about these flip-flopping between resolutions.
+ switch (userdata->pixel_format) {
+ case bmusb::PixelFormat_10BitYCbCr:
+ glBindTexture(GL_TEXTURE_2D, userdata->tex_444[field]);
+ check_error();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, nullptr);
+ check_error();
+ break;
+ case bmusb::PixelFormat_8BitYCbCr: {
+ size_t cbcr_width = width / 2;
+
+ glBindTexture(GL_TEXTURE_2D, userdata->tex_cbcr[field]);
+ check_error();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, cbcr_width, height, 0, GL_RG, GL_UNSIGNED_BYTE, nullptr);
+ check_error();
+ glBindTexture(GL_TEXTURE_2D, userdata->tex_y[field]);
+ check_error();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
+ check_error();
+ break;
+ }
+ case bmusb::PixelFormat_8BitBGRA:
+ glBindTexture(GL_TEXTURE_2D, userdata->tex_rgba[field]);
+ check_error();
+ if (global_flags.can_disable_srgb_decoder) { // See the comments in tweaked_inputs.h.
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
+ } else {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
+ }
+ check_error();
+ break;
+ }
+ userdata->last_width[field] = width;
+ userdata->last_height[field] = height;
+ }
+ if (global_flags.ten_bit_input &&
+ (first || v210_width != userdata->last_v210_width[field])) {
+ // Same as above; we need to recreate the texture.
+ glBindTexture(GL_TEXTURE_2D, userdata->tex_v210[field]);
+ check_error();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, v210_width, height, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, nullptr);
+ check_error();
+ userdata->last_v210_width[field] = v210_width;
+ }
+}
+
+void upload_texture(GLuint tex, GLuint width, GLuint height, GLuint stride, bool interlaced_stride, GLenum format, GLenum type, GLintptr offset)
+{
+ if (interlaced_stride) {
+ stride *= 2;
+ }
+ if (global_flags.flush_pbos) {
+ glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, offset, stride * height);
+ check_error();
+ }
+
+ glBindTexture(GL_TEXTURE_2D, tex);
+ check_error();
+ if (interlaced_stride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width * 2);
+ check_error();
+ } else {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ check_error();
+ }
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, BUFFER_OFFSET(offset));
+ check_error();
+ glBindTexture(GL_TEXTURE_2D, 0);
+ check_error();
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ check_error();
+}
+