]> git.sesse.net Git - nageru/blob - pbo_frame_allocator.cpp
Do not link kaeru against CEF.
[nageru] / pbo_frame_allocator.cpp
1 #include "pbo_frame_allocator.h"
2
3 #include <bmusb/bmusb.h>
4 #include <movit/util.h>
5 #include <stdbool.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <cstddef>
9
10 #include "flags.h"
11 #include "v210_converter.h"
12
13 using namespace std;
14
15 namespace {
16
17 void set_clamp_to_edge()
18 {
19         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
20         check_error();
21         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
22         check_error();
23         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
24         check_error();
25 }
26
27 }  // namespace
28
29 PBOFrameAllocator::PBOFrameAllocator(bmusb::PixelFormat pixel_format, size_t frame_size, GLuint width, GLuint height, size_t num_queued_frames, GLenum buffer, GLenum permissions, GLenum map_bits)
30         : pixel_format(pixel_format), buffer(buffer)
31 {
32         userdata.reset(new Userdata[num_queued_frames]);
33         for (size_t i = 0; i < num_queued_frames; ++i) {
34                 GLuint pbo;
35                 glGenBuffers(1, &pbo);
36                 check_error();
37                 glBindBuffer(buffer, pbo);
38                 check_error();
39                 glBufferStorage(buffer, frame_size, nullptr, permissions | GL_MAP_PERSISTENT_BIT);
40                 check_error();
41
42                 Frame frame;
43                 frame.data = (uint8_t *)glMapBufferRange(buffer, 0, frame_size, permissions | map_bits | GL_MAP_PERSISTENT_BIT);
44                 frame.data2 = frame.data + frame_size / 2;
45                 check_error();
46                 frame.size = frame_size;
47                 frame.userdata = &userdata[i];
48                 userdata[i].pbo = pbo;
49                 userdata[i].pixel_format = pixel_format;
50                 frame.owner = this;
51
52                 // For 8-bit non-planar Y'CbCr, we ask the driver to split Y' and Cb/Cr
53                 // into separate textures. For 10-bit, the input format (v210)
54                 // is complicated enough that we need to interpolate up to 4:4:4,
55                 // which we do in a compute shader ourselves. For BGRA, the data
56                 // is already 4:4:4:4.
57                 frame.interleaved = (pixel_format == bmusb::PixelFormat_8BitYCbCr);
58
59                 // Create textures. We don't allocate any data for the second field at this point
60                 // (just create the texture state with the samplers), since our default assumed
61                 // resolution is progressive.
62                 switch (pixel_format) {
63                 case bmusb::PixelFormat_8BitYCbCr:
64                         glGenTextures(2, userdata[i].tex_y);
65                         check_error();
66                         glGenTextures(2, userdata[i].tex_cbcr);
67                         check_error();
68                         break;
69                 case bmusb::PixelFormat_10BitYCbCr:
70                         glGenTextures(2, userdata[i].tex_v210);
71                         check_error();
72                         glGenTextures(2, userdata[i].tex_444);
73                         check_error();
74                         break;
75                 case bmusb::PixelFormat_8BitBGRA:
76                         glGenTextures(2, userdata[i].tex_rgba);
77                         check_error();
78                         break;
79                 case bmusb::PixelFormat_8BitYCbCrPlanar:
80                         glGenTextures(2, userdata[i].tex_y);
81                         check_error();
82                         glGenTextures(2, userdata[i].tex_cb);
83                         check_error();
84                         glGenTextures(2, userdata[i].tex_cr);
85                         check_error();
86                         break;
87                 default:
88                         assert(false);
89                 }
90
91                 userdata[i].last_width[0] = width;
92                 userdata[i].last_height[0] = height;
93                 userdata[i].last_cbcr_width[0] = width / 2;
94                 userdata[i].last_cbcr_height[0] = height;
95                 userdata[i].last_v210_width[0] = 0;
96
97                 userdata[i].last_width[1] = 0;
98                 userdata[i].last_height[1] = 0;
99                 userdata[i].last_cbcr_width[1] = 0;
100                 userdata[i].last_cbcr_height[1] = 0;
101                 userdata[i].last_v210_width[1] = 0;
102
103                 userdata[i].last_interlaced = false;
104                 userdata[i].last_has_signal = false;
105                 userdata[i].last_is_connected = false;
106                 for (unsigned field = 0; field < 2; ++field) {
107                         switch (pixel_format) {
108                         case bmusb::PixelFormat_10BitYCbCr: {
109                                 const size_t v210_width = v210Converter::get_minimum_v210_texture_width(width);
110
111                                 // Seemingly we need to set the minification filter even though
112                                 // shader image loads don't use them, or NVIDIA will just give us
113                                 // zero back.
114                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_v210[field]);
115                                 check_error();
116                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
117                                 check_error();
118                                 if (field == 0) {
119                                         userdata[i].last_v210_width[0] = v210_width;
120                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, v210_width, height, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, nullptr);
121                                         check_error();
122                                 }
123
124                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_444[field]);
125                                 check_error();
126                                 set_clamp_to_edge();
127                                 if (field == 0) {
128                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, nullptr);
129                                         check_error();
130                                 }
131                                 break;
132                         }
133                         case bmusb::PixelFormat_8BitYCbCr:
134                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_y[field]);
135                                 check_error();
136                                 set_clamp_to_edge();
137                                 if (field == 0) {
138                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
139                                         check_error();
140                                 }
141
142                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_cbcr[field]);
143                                 check_error();
144                                 set_clamp_to_edge();
145                                 if (field == 0) {
146                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width / 2, height, 0, GL_RG, GL_UNSIGNED_BYTE, nullptr);
147                                         check_error();
148                                 }
149                                 break;
150                         case bmusb::PixelFormat_8BitBGRA:
151                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_rgba[field]);
152                                 check_error();
153                                 set_clamp_to_edge();
154                                 if (field == 0) {
155                                         if (global_flags.can_disable_srgb_decoder) {  // See the comments in tweaked_inputs.h.
156                                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
157                                         } else {
158                                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
159                                         }
160                                         check_error();
161                                 }
162                                 break;
163                         case bmusb::PixelFormat_8BitYCbCrPlanar:
164                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_y[field]);
165                                 check_error();
166                                 set_clamp_to_edge();
167                                 if (field == 0) {
168                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
169                                         check_error();
170                                 }
171
172                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_cb[field]);
173                                 check_error();
174                                 set_clamp_to_edge();
175                                 if (field == 0) {
176                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width / 2, height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
177                                         check_error();
178                                 }
179
180                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_cr[field]);
181                                 check_error();
182                                 set_clamp_to_edge();
183                                 if (field == 0) {
184                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width / 2, height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
185                                         check_error();
186                                 }
187                                 break;
188                         default:
189                                 assert(false);
190                         }
191                 }
192
193                 freelist.push(frame);
194         }
195         glBindBuffer(buffer, 0);
196         check_error();
197         glBindTexture(GL_TEXTURE_2D, 0);
198         check_error();
199 }
200
201 PBOFrameAllocator::~PBOFrameAllocator()
202 {
203         while (!freelist.empty()) {
204                 Frame frame = freelist.front();
205                 freelist.pop();
206                 GLuint pbo = ((Userdata *)frame.userdata)->pbo;
207                 glBindBuffer(buffer, pbo);
208                 check_error();
209                 glUnmapBuffer(buffer);
210                 check_error();
211                 glBindBuffer(buffer, 0);
212                 check_error();
213                 glDeleteBuffers(1, &pbo);
214                 check_error();
215                 switch (pixel_format) {
216                 case bmusb::PixelFormat_10BitYCbCr:
217                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_v210);
218                         check_error();
219                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_444);
220                         check_error();
221                         break;
222                 case bmusb::PixelFormat_8BitYCbCr:
223                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_y);
224                         check_error();
225                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_cbcr);
226                         check_error();
227                         break;
228                 case bmusb::PixelFormat_8BitBGRA:
229                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_rgba);
230                         check_error();
231                         break;
232                 case bmusb::PixelFormat_8BitYCbCrPlanar:
233                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_y);
234                         check_error();
235                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_cb);
236                         check_error();
237                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_cr);
238                         check_error();
239                         break;
240                 default:
241                         assert(false);
242                 }
243         }
244 }
245 //static int sumsum = 0;
246
247 bmusb::FrameAllocator::Frame PBOFrameAllocator::alloc_frame()
248 {
249         Frame vf;
250
251         unique_lock<mutex> lock(freelist_mutex);  // Meh.
252         if (freelist.empty()) {
253                 printf("Frame overrun (no more spare PBO frames), dropping frame!\n");
254         } else {
255                 //fprintf(stderr, "freelist has %d allocated\n", ++sumsum);
256                 vf = freelist.front();
257                 freelist.pop();  // Meh.
258         }
259         vf.len = 0;
260         vf.overflow = 0;
261         return vf;
262 }
263
264 void PBOFrameAllocator::release_frame(Frame frame)
265 {
266         if (frame.overflow > 0) {
267                 printf("%d bytes overflow after last (PBO) frame\n", int(frame.overflow));
268         }
269
270 #if 0
271         // Poison the page. (Note that this might be bogus if you don't have an OpenGL context.)
272         memset(frame.data, 0, frame.size);
273         Userdata *userdata = (Userdata *)frame.userdata;
274         for (unsigned field = 0; field < 2; ++field) {
275                 glBindTexture(GL_TEXTURE_2D, userdata->tex_y[field]);
276                 check_error();
277                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
278                 check_error();
279                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
280                 check_error();
281                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
282                 check_error();
283                 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, userdata->last_width[field], userdata->last_height[field], 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
284                 check_error();
285
286                 glBindTexture(GL_TEXTURE_2D, userdata->tex_cbcr[field]);
287                 check_error();
288                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
289                 check_error();
290                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
291                 check_error();
292                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
293                 check_error();
294                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, userdata->last_width[field] / 2, userdata->last_height[field], 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
295                 check_error();
296         }
297 #endif
298
299         unique_lock<mutex> lock(freelist_mutex);
300         freelist.push(frame);
301         //--sumsum;
302 }