]> git.sesse.net Git - nageru/blob - pbo_frame_allocator.cpp
Support 10-bit capture, both on bmusb and on DeckLink drivers.
[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
12 using namespace std;
13
14 PBOFrameAllocator::PBOFrameAllocator(size_t frame_size, GLuint width, GLuint height, size_t num_queued_frames, GLenum buffer, GLenum permissions, GLenum map_bits)
15         : buffer(buffer)
16 {
17         userdata.reset(new Userdata[num_queued_frames]);
18         for (size_t i = 0; i < num_queued_frames; ++i) {
19                 GLuint pbo;
20                 glGenBuffers(1, &pbo);
21                 check_error();
22                 glBindBuffer(buffer, pbo);
23                 check_error();
24                 glBufferStorage(buffer, frame_size, NULL, permissions | GL_MAP_PERSISTENT_BIT);
25                 check_error();
26
27                 Frame frame;
28                 frame.data = (uint8_t *)glMapBufferRange(buffer, 0, frame_size, permissions | map_bits | GL_MAP_PERSISTENT_BIT);
29                 frame.data2 = frame.data + frame_size / 2;
30                 check_error();
31                 frame.size = frame_size;
32                 frame.userdata = &userdata[i];
33                 userdata[i].pbo = pbo;
34                 frame.owner = this;
35
36                 // For 8-bit Y'CbCr, we ask the driver to split Y' and Cb/Cr
37                 // into separate textures. For 10-bit, the input format (v210)
38                 // is complicated enough that we need to interpolate up to 4:4:4,
39                 // which we do in a compute shader ourselves.
40                 frame.interleaved = !global_flags.ten_bit_input;
41
42                 // Create textures. We don't allocate any data for the second field at this point
43                 // (just create the texture state with the samplers), since our default assumed
44                 // resolution is progressive.
45                 if (global_flags.ten_bit_input) {
46                         glGenTextures(2, userdata[i].tex_v210);
47                         check_error();
48                         glGenTextures(2, userdata[i].tex_444);
49                         check_error();
50                 } else {
51                         glGenTextures(2, userdata[i].tex_y);
52                         check_error();
53                         glGenTextures(2, userdata[i].tex_cbcr);
54                         check_error();
55                 }
56                 userdata[i].last_width[0] = width;
57                 userdata[i].last_height[0] = height;
58                 userdata[i].last_width[1] = 0;
59                 userdata[i].last_height[1] = 0;
60                 userdata[i].last_interlaced = false;
61                 userdata[i].last_has_signal = false;
62                 userdata[i].last_is_connected = false;
63                 for (unsigned field = 0; field < 2; ++field) {
64                         if (global_flags.ten_bit_input) {
65                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_v210[field]);
66                                 check_error();
67                                 // Don't care about texture parameters, we're only going to read it
68                                 // from the compute shader anyway.
69                                 if (field == 0) {
70                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, NULL);
71                                         check_error();
72                                 }
73
74                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_444[field]);
75                                 check_error();
76                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
77                                 check_error();
78                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
79                                 check_error();
80                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
81                                 check_error();
82                                 if (field == 0) {
83                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, NULL);
84                                         check_error();
85                                 }
86                         } else {
87                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_y[field]);
88                                 check_error();
89                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
90                                 check_error();
91                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
92                                 check_error();
93                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
94                                 check_error();
95                                 if (field == 0) {
96                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
97                                         check_error();
98                                 }
99
100                                 glBindTexture(GL_TEXTURE_2D, userdata[i].tex_cbcr[field]);
101                                 check_error();
102                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
103                                 check_error();
104                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
105                                 check_error();
106                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
107                                 check_error();
108                                 if (field == 0) {
109                                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width / 2, height, 0, GL_RG, GL_UNSIGNED_BYTE, NULL);
110                                         check_error();
111                                 }
112                         }
113                 }
114
115                 freelist.push(frame);
116         }
117         glBindBuffer(buffer, 0);
118         check_error();
119         glBindTexture(GL_TEXTURE_2D, 0);
120         check_error();
121 }
122
123 PBOFrameAllocator::~PBOFrameAllocator()
124 {
125         while (!freelist.empty()) {
126                 Frame frame = freelist.front();
127                 freelist.pop();
128                 GLuint pbo = ((Userdata *)frame.userdata)->pbo;
129                 glBindBuffer(buffer, pbo);
130                 check_error();
131                 glUnmapBuffer(buffer);
132                 check_error();
133                 glBindBuffer(buffer, 0);
134                 check_error();
135                 glDeleteBuffers(1, &pbo);
136                 check_error();
137                 if (global_flags.ten_bit_input) {
138                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_v210);
139                         check_error();
140                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_444);
141                         check_error();
142                 } else {
143                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_y);
144                         check_error();
145                         glDeleteTextures(2, ((Userdata *)frame.userdata)->tex_cbcr);
146                         check_error();
147                 }
148         }
149 }
150 //static int sumsum = 0;
151
152 bmusb::FrameAllocator::Frame PBOFrameAllocator::alloc_frame()
153 {
154         Frame vf;
155
156         unique_lock<mutex> lock(freelist_mutex);  // Meh.
157         if (freelist.empty()) {
158                 printf("Frame overrun (no more spare PBO frames), dropping frame!\n");
159         } else {
160                 //fprintf(stderr, "freelist has %d allocated\n", ++sumsum);
161                 vf = freelist.front();
162                 freelist.pop();  // Meh.
163         }
164         vf.len = 0;
165         vf.overflow = 0;
166         return vf;
167 }
168
169 void PBOFrameAllocator::release_frame(Frame frame)
170 {
171         if (frame.overflow > 0) {
172                 printf("%d bytes overflow after last (PBO) frame\n", int(frame.overflow));
173         }
174
175 #if 0
176         // Poison the page. (Note that this might be bogus if you don't have an OpenGL context.)
177         memset(frame.data, 0, frame.size);
178         Userdata *userdata = (Userdata *)frame.userdata;
179         for (unsigned field = 0; field < 2; ++field) {
180                 glBindTexture(GL_TEXTURE_2D, userdata->tex_y[field]);
181                 check_error();
182                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
183                 check_error();
184                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
185                 check_error();
186                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
187                 check_error();
188                 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, userdata->last_width[field], userdata->last_height[field], 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
189                 check_error();
190
191                 glBindTexture(GL_TEXTURE_2D, userdata->tex_cbcr[field]);
192                 check_error();
193                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
194                 check_error();
195                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
196                 check_error();
197                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
198                 check_error();
199                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, userdata->last_width[field] / 2, userdata->last_height[field], 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
200                 check_error();
201         }
202 #endif
203
204         unique_lock<mutex> lock(freelist_mutex);
205         freelist.push(frame);
206         //--sumsum;
207 }