+ init_frame(i, frame_size, width, height, permissions, map_bits);
+ }
+ glBindBuffer(buffer, 0);
+ check_error();
+ glBindTexture(GL_TEXTURE_2D, 0);
+ check_error();
+}
+
+void PBOFrameAllocator::init_frame(size_t frame_idx, size_t frame_size, GLuint width, GLuint height, GLenum permissions, GLenum map_bits)
+{
+ GLuint pbo;
+ glGenBuffers(1, &pbo);
+ check_error();
+ glBindBuffer(buffer, pbo);
+ check_error();
+ glBufferStorage(buffer, frame_size, nullptr, permissions | GL_MAP_PERSISTENT_BIT);
+ check_error();
+
+ Frame frame;
+ frame.data = (uint8_t *)glMapBufferRange(buffer, 0, frame_size, permissions | map_bits | GL_MAP_PERSISTENT_BIT);
+ frame.data2 = frame.data + frame_size / 2;
+ frame.data_copy = new uint8_t[frame_size];
+ check_error();
+ frame.size = frame_size;
+ frame.userdata = &userdata[frame_idx];
+ userdata[frame_idx].pbo = pbo;
+ userdata[frame_idx].pixel_format = pixel_format;
+ frame.owner = this;
+
+ // For 8-bit non-planar Y'CbCr, we ask the driver to split Y' and Cb/Cr
+ // into separate textures. For 10-bit, the input format (v210)
+ // is complicated enough that we need to interpolate up to 4:4:4,
+ // which we do in a compute shader ourselves. For BGRA, the data
+ // is already 4:4:4:4.
+ frame.interleaved = (pixel_format == bmusb::PixelFormat_8BitYCbCr);
+
+ // Create textures. We don't allocate any data for the second field at this point
+ // (just create the texture state with the samplers), since our default assumed
+ // resolution is progressive.
+ switch (pixel_format) {
+ case bmusb::PixelFormat_8BitYCbCr:
+ glGenTextures(2, userdata[frame_idx].tex_y);